[C++-sig] Missing definitions for virtual methods in Pyste-generated code

Nicodemus nicodemus at globalite.com.br
Fri Mar 21 21:54:28 CET 2003


David Abrahams wrote:

>Patrick Hartling <patrick at vrac.iastate.edu> writes:
>
>  
>
>>In my continued experimentation with Pyste, I have run into a problem
>>relating to virtual methods.  I have attached example code that
>>demonstrates the problem specifically, but I will try to explain it in
>>general terms here.
>>
>>First, the class structure is similar to this:
>>
>>class A
>>{
>>public:
>>    virtual void f1();
>>};
>>
>>class B : public A
>>{
>>public:
>>    virtual void f2();
>>};
>>
>>My goal, then, is to derive from B a class C written in Python:
>>
>>class C(MyModule.B):
>>    def __init__(self):
>>       MyModule.B.__init__(self)
>>
>>    def f1(self):
>>       # Do something ...
>>
>>    def f2(self):
>>       # Do something else ...
>>
>>Ultimately, an instance of C will be handed off to some C++ code where
>>it needs to be treated polymorphically as an instance of either A or
>>B, depending on the context.
>>
>>In Pyste, I declare that I want to expose both A and B to
>>Python. However, the Boost.Python code that gets generated for class B
>>is missing the definition for the f1() method that is inherited from
>>class A:
>>
>>class_< B, bases< A > , boost::noncopyable, B_Wrapper >("B", init<  >())
>>     .def("f2", &B::f2, &B_Wrapper::default_f2)
>>;
>>
>>Without the definition for the f1() member function, C.f1() is never
>>invoked by the C++ code.  C.f2() works fine, however.  I can easily
>>modify the generated Boost.Python code to add the definition of f1(),
>>but this seems like a bug in how Pyste deals with inherited member
>>functions.
>>    
>>
>
>Something is slightly wrong with your analysis:
>
>1. The generated code for wrapping A should contain a definition of f1
>
>2. That's not what causes C.f1 to be called when invoked from C++
>   anyway: it's the override in the wrapper class for A which I
>   presume from reading the code above should be called A_Wrapper
>   that makes sure C.f1 gets called.
>
>...and now I see the problem.  For scenarios like this, the Wrapper
>classes really need to use virtual inheritance.  It should look
>something like this:
>
>struct A_Wrapper : virtual A
>{
>    A_Wrapper(PyObject* self) : self(self) {}
>    void f1() { call_method<void>(self, "f1"); }
>    void A_f1() { this->A::f1(); }
>};
>
>// note inheritance from A_Wrapper
>struct B_Wrapper : A_Wrapper, virtual B 
>{
>    B_Wrapper(PyObject* self) : self(self) {}
>    void f2() { call_method<void>(self, "f2"); }
>    void B_f2() { this->B::f2(); }
>};
>
>HTH,
>
Unfortunetely I get the same output, even after this changes. From what 
I understand, they should work too. Do you have any other idea Dave? I 
cleaned the examples from Patrick a bit, and here are they for reference:

------------ test.h

#include <iostream>

using namespace std;

struct A
{
    virtual void f1() { cout << "A::f1" << endl; }
};

struct B: public A
{
    virtual void f2() { cout << "B::f2" << endl; }
};

void call(A* a)
{
    a->f1();
    B* b = dynamic_cast<B*>(a);
    b->f2();
}



------------ test.cpp (generated, and then manual-changed)


#include <boost/python.hpp>
#include <test.h>

using namespace boost::python;

struct A_Wrapper: virtual A
{
    A_Wrapper(PyObject* self_, const A & p0):
        A(p0), self(self_) {}

    A_Wrapper(PyObject* self_):
        A(), self(self_) {}

    void f1() {
        call_method< void >(self, "f1");
    }

    void default_f1() {
        A::f1();
    }

    PyObject* self;
};

struct B_Wrapper: virtual B, A_Wrapper
{
    B_Wrapper(PyObject* self_, const B & p0):
        B(p0), A_Wrapper(self_), self(self_) {}

    B_Wrapper(PyObject* self_):
        B(), A_Wrapper(self_), self(self_) {}

    void f2() {
        call_method< void >(self, "f2");
    }

    void default_f2() {
        B::f2();
    }

    PyObject* self;
};


BOOST_PYTHON_MODULE(test)
{
    def("call", &call);
    class_< A, A_Wrapper >("A", init<  >())
        .def(init< const A & >())
        .def("f1", &A::f1, &A_Wrapper::default_f1)
    ;

    class_< B, bases< A > , B_Wrapper >("B", init<  >())
        .def(init< const B & >())
        .def("f2", &B::f2, &B_Wrapper::default_f2)
    ;
} 



----------------- test.py

from test import *

class C(B):
    def __init__(self):
        B.__init__(self)

    def f1(self): print 'C.f1'
    def f2(self): print 'C.f2'


call(C())


------------------- output
A::f1
C.f2






More information about the Cplusplus-sig mailing list