virtual method issue ?
Dear all, I'm exposing a C++ library to python using boost python. Almost everything works fine except this: I have a Minimizer class that needs a "function" object to minimize. These objects communicates with std::vector<double> and I think the problem is around there... I had to (partially) expose std::vector<double> to python because of a lvalue convertor problem. The function object is derived from an abstract class "Base". When I try to redefine the function in python, the std::vector doesn't seem to be correctly transmitted between objects. You will find bellow the minimal example I found to reproduce the undesired feature... Any advice would be really appreciated ! Thanks, Adrien ============ C++ CODE =============== #include <boost/python.hpp> #include <boost/python/module.hpp> #include <boost/python/class.hpp> #include <boost/python/wrapper.hpp> #include <boost/python/call.hpp> #include <boost/python/suite/indexing/indexing_suite.hpp> #include <boost/python/suite/indexing/vector_indexing_suite.hpp> #include <iostream> #include <vector> #include <cassert> typedef std::vector<double> Vdouble; using namespace boost::python; struct Base { virtual ~Base(){}; virtual double Function(const Vdouble& X, Vdouble& Grad)=0; }; struct BaseWrap: Base, wrapper<Base> { double Function(const Vdouble& v, Vdouble& Grad) { return this->get_override("Function")(v,Grad); } }; struct Minimizer { Minimizer(Base& base):_base(base) { _vec.resize(5); _Grad.resize(5); for (uint i=0; i<5; i++) {_vec[i]=0.0; _Grad[i]=0.0;} }; void minimize(){ _base.Function(_vec, _Grad); std::cout << "_Grad[0] =" << _Grad[0] << std::endl; assert(_Grad[0]>=5.0); } //Data: Base& _base; Vdouble _vec; Vdouble _Grad; }; struct Derived: public Base { double Function(const Vdouble& v, Vdouble& grad) { grad[0]=12; return v[0]; } }; using namespace boost::python; BOOST_PYTHON_MODULE(minim) { class_<std::vector<double> >("std_vector_double") .def(vector_indexing_suite<std::vector<double> >()) ; class_<BaseWrap, boost::noncopyable>("Base") .def("Function",pure_virtual(&Base::Function)); class_<Derived, bases<Base> >("Derived") .def("Function",&Derived::Function ); class_<Minimizer>("Minimizer", init< Base& >() ) .def("minimize", &Minimizer::minimize); } ========== END C++ CODE ======== compilation: g++ -o minim.so -shared minim.cpp -I/usr/include/python2.4 -lboost_python -lpython2.4 ========== PYTHON CODE: ========= from minim import * class PyDerived(Base): def Function(self, v, grad): grad[0]=45.0 return 0 pyderived=PyDerived() v=std_vector_double() grad=std_vector_double() for i in range(5): v.append(0.0) grad.append(0.0) derived=Derived() minim=Minimizer(derived) minim.minimize() #this one is OK assert grad[0]>5.0, "grad[0]>5.0" #this one is OK pyderived.Function(v,grad) print grad[0] assert grad[0]==45.0, "grad[0]==45.0" #this one is OK minim=Minimizer(pyderived) minim.minimize() # <--- Here I get a C++ assert error (not OK...) =========== END PYTHON CODE ==================
On 3/15/07, Adrien Saladin <adrien-ml@wizzi.net> wrote:
Dear all,
I'm exposing a C++ library to python using boost python. Almost everything works fine except this:
I have a Minimizer class that needs a "function" object to minimize. These objects communicates with std::vector<double> and I think the problem is around there...
I had to (partially) expose std::vector<double> to python because of a lvalue convertor problem.
The function object is derived from an abstract class "Base". When I try to redefine the function in python, the std::vector doesn't seem to be correctly transmitted between objects.
You will find bellow the minimal example I found to reproduce the undesired feature... Any advice would be really appreciated !
Your code contains one error and few places where better code could be created: 1. Minimizer(Base& base) - when you expose this constructor you'd better use call policies, otherwise you will have to keep base instance somewhere And the error: class PyDerived(Base): def Function(self, v, grad): grad[0]=45.0 return 0 You have to call __init__ method of Base class class PyDerived(Base): def __init__( self ): Base.__init__( self ) def Function(self, v, grad): grad[0]=45.0 return 0 This should fix the assert you get. P.S. This error comes too often. Is it possible to add an exception, which will give a hint to a user what should be done? Thanks -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
Le jeudi 15 mars 2007 21:10, Roman Yakovenko a écrit :
Your code contains one error and few places where better code could be created:
1. Minimizer(Base& base) - when you expose this constructor you'd better use call policies, otherwise you will have to keep base instance somewhere
Hello, Thanks for the advice, I will probably have to modify my c++ code. Here the problem is the same than in C++ right ? If a C++ or python function returns a Minimizer instance and if the Base instance has been created "on the stack" in this function, it will reference a non-existing object. Which call policy is the more adequate in this case ?
And the error:
class PyDerived(Base): def Function(self, v, grad): grad[0]=45.0 return 0
You have to call __init__ method of Base class
class PyDerived(Base): def __init__( self ): Base.__init__( self )
def Function(self, v, grad): grad[0]=45.0 return 0
This should fix the assert you get.
I changed the PyDerived class to this new one and I still get the C++ assert: _Grad[0] =0 python: minim.cpp:49: void Minimizer::minimize(): Assertion `_Grad[0]>=5.0' failed. Best regards, Adrien
On 3/16/07, Adrien Saladin <adrien-ml@wizzi.net> wrote:
_Grad[0] =0 python: minim.cpp:49: void Minimizer::minimize(): Assertion `_Grad[0]>=5.0' failed.
struct BaseWrap: Base, wrapper<Base> { double Function(const Vdouble& v, Vdouble& Grad) { return this->get_override("Function")(v,Grad); } }; This code should be changed to return this->get_override("Function")(boost::ref(v), boost::ref(Grad)); By default boost::python::object holds a copy of the instance. The code generator I created, Py++ is aware to this and generates right code. It has GUI, so you can create simple test case and see what code it will generate. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
Le vendredi 16 mars 2007 11:24, Roman Yakovenko a écrit :
This code should be changed to
return this->get_override("Function")(boost::ref(v), boost::ref(Grad));
By default boost::python::object holds a copy of the instance.
It works perfectly now. Thanks !
The code generator I created, Py++ is aware to this and generates right code. It has GUI, so you can create simple test case and see what code it will generate.
I've installed Py++ and I think I will learn many things from it. Best regards, Adrien
participants (2)
-
Adrien Saladin -
Roman Yakovenko