[C++-sig] RuntimeError(Pure virtual function called) when using iterator with abstract class

Mr Kun Hong kun.hong at uqconnect.edu.au
Mon May 17 15:41:14 CEST 2010


Hi all,

I am new to Booth Python. I am trying to make a python wrapper for one of my job's C++ library.
But after reading the docs and related mailing list archive, I am still stucked. :( If any one can
shed some light on me, it is greatly appreciated.

Below is a simplified version of the library I am trying to wrap. The design is basically an abstract
class A (with factory method to return implementation instance) which provides an iterator interface.
The iterator also returns an abstract class Position instances, which is supposed to be implemented by
subclasses of A. Please see be code (it is a bit long, thanks for you patience).

=== test.h:



#include <iterator>

#include <boost/shared_ptr.hpp>
#include <boost/python.hpp>


class A
{
public:

    class Position
    {
    public:
        virtual ~Position() {};
        virtual bool exists() const = 0;
        virtual int getId() const = 0;
    };

    // forward declaration
    class AIterator;

    class AIteratorBase
        : public std::iterator<std::bidirectional_iterator_tag,
                                const A::Position,
                                std::ptrdiff_t,
                                const A::Position*,
                                const A::Position&>
    {
    public:
        virtual ~AIteratorBase() {}
        virtual boost::shared_ptr<AIteratorBase> clone() const = 0;
        virtual reference operator*() const = 0;
        virtual pointer operator->() const = 0;
        virtual void increment() = 0;
        virtual void decrement() = 0;
        virtual bool operator==(const AIterator& other) const = 0;
    };

    class AIterator
        : public std::iterator<std::bidirectional_iterator_tag,
                               const A::Position,
                               std::ptrdiff_t,
                               const A::Position*,
                               const A::Position&>
    {
    public:
        AIterator(boost::shared_ptr<AIteratorBase>& priv)
            : mPriv(priv)
        { }

        AIterator(const AIterator& other)
            : mPriv(other.mPriv->clone())
        { }

        AIterator& operator=(const AIterator& other)
        {
            boost::shared_ptr<AIteratorBase> clone = other.mPriv->clone();
            mPriv = clone;
            return *this;
        }

        reference operator*() const
        {
            return mPriv->operator*();
        }

        pointer operator->() const
        {
            return mPriv->operator->();
        }

        AIterator& operator++()
        {
            mPriv->increment();
            return *this;
        }

        AIterator operator++(int)
        {
            boost::shared_ptr<AIteratorBase> privClone = mPriv->clone();
            AIterator toReturn(privClone);
            mPriv->increment();
            return toReturn;
        }

        AIterator& operator--()
        {
            mPriv->decrement();
            return *this;
        }
        AIterator operator--(int)
        {
            boost::shared_ptr<AIteratorBase> privClone = mPriv->clone();
            AIterator toReturn(privClone);
            mPriv->decrement();
            return toReturn;
        }

        bool operator==(const AIterator& other) const
        {
            return mPriv->operator==(other);
        }

        bool operator!=(const AIterator& other) const
        {
            return !operator==(other);
        }

    private:
        boost::shared_ptr<AIteratorBase> mPriv;
    };

    static boost::shared_ptr<A> getAInstance(int type);

    virtual AIterator begin(int row) const = 0;
    virtual AIterator end(int row) const = 0;
};

class AOdd : public A
{
public:
    class Position : public A::Position
    {
    public:
        Position(int id) : mId(id) {}
        virtual ~Position() {}
        virtual bool exists() const { return (mId % 2 != 0); }
        virtual int getId() const { return mId; }

        int mId;
    };

    class AOddIterator
        : public A::AIteratorBase
    {
    public:
        AOddIterator(int pos) : mPos(pos) {}

        virtual ~AOddIterator() {}

        virtual boost::shared_ptr<AIteratorBase> clone() const
        {
            return boost::shared_ptr<A::AIteratorBase>(
                    new AOddIterator(*this));
        }

        virtual reference operator*() const
        {
            return mPos;
        }
        virtual pointer operator->() const
        {
            return &mPos;
        }

        virtual void increment() { mPos.mId++; }
        virtual void decrement() { mPos.mId--; }

        virtual bool operator==(const AIterator& other) const
        {
            return mPos.getId() == (*other).getId();
        }

    private:
        Position mPos;
    };

    virtual AIterator begin(int row) const
    {
        boost::shared_ptr<A::AIteratorBase> iterPriv
            (new AOdd::AOddIterator(row * 1024));
        return A::AIterator(iterPriv);
    }
    virtual AIterator end(int row) const
    {
        boost::shared_ptr<A::AIteratorBase> iterPriv
            (new AOdd::AOddIterator((row + 1) * 1024));
        return A::AIterator(iterPriv);
    }
};


boost::shared_ptr<A> A::getAInstance(int type)
{
    // return an instance base on type value, but only return ADerived here.
    static boost::shared_ptr<A> aInstance(new AOdd());
    return aInstance;
}


=== test.cpp (wrapping code)

#include "test.h"

#include <boost/python.hpp>

using namespace boost::python;

boost::python::object
aIterator(A const &a, int row)
{
    return
        //range<return_value_policy<return_by_value>, A>(
        range<return_internal_reference<>, A>(
            boost::bind(&A::begin, _1, row),
            boost::bind(&A::end, _1, row)
        )(boost::ref(a));
}

class PositionWrap : public A::Position, public wrapper<A::Position>
{
public:
    virtual int getId() { return this->get_override("getId")(); }
    virtual bool exists() { return this->get_override("exists")(); }
};

BOOST_PYTHON_MODULE(test)
{
    scope as = class_<A, boost::shared_ptr<A>, boost::noncopyable>
               ("A", no_init)
               .def("getAInstance", &A::getAInstance)
               .staticmethod("getAInstance")

               .def("getIterator", &aIterator)
    ;

    class_<A::Position, boost::noncopyable>
        ("Position", no_init)
        .def("exists", pure_virtual(&A::Position::exists))
        .def("getId", pure_virtual(&A::Position::getId))
    ;

}


=== main.cpp (code for demonstrating the use of the C++ library)

#include "test.h"

#include <iostream>

using namespace std;

int main()
{
    A *a = (A::getAInstance(0)).get();

    for(A::AIterator begin = a->begin(0); begin != a->end(0); begin++)
    {
        cout << begin->exists() << endl;
    }

    return 0;
}


=== test.py (Code for tesing the python extension)

from test import *

a = A.getAInstance(0)
for x in a.getIterator(0):
    #print type(x)
    print x.exists()


=== end


Thanks a lot for you patience, since you read this far...
So now, I run the test.py code, which is fine if I just "print type(x)"
which did print correctly as "<class 'test.Position'>".

But when I tried to do "print x.exists()", it gave this error:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print x.exists()
RuntimeError: Pure virtual function called

It is supposed to call the solid subclass AOdd::Position code,
just like the C++ main.cpp code does. Why it doesn't work?

It could be something very simple and stupid, that I did wrong.
If you spot it, please help me.  Thanks a lot!

Kun





More information about the Cplusplus-sig mailing list