Making copy of function derived from C++ pure virtual function
I have a function written in Python derived from C++. Now I want to clone this function in C++. What should I do in the copy constructor of the function wrapper? I have... FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap ) : FunctionBase ( wrap ), m_self ( wrap.m_self ) { // what should be here to create a new PyObject that is copy of old one? } FunctionBase * FunctionWrap:: clone () const { return new FunctionWrap ( *this ); }
Paul F. Kunz wrote:
I have a function written in Python derived from C++. Now I want to clone this function in C++. What should I do in the copy constructor of the function wrapper? I have...
Way too much detail missing for me to say anything useful. Start by defining your terms (or used established terminology): What does it mean to have a "function written in Python derived from C++?" What does it mean to clone a function?
FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap ) : FunctionBase ( wrap ), m_self ( wrap.m_self ) { // what should be here to create a new PyObject that is copy of old one?
What old one?
}
FunctionBase * FunctionWrap:: clone () const { return new FunctionWrap ( *this ); }
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Wed, 08 Dec 2004 14:44:52 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
I have a function written in Python derived from C++. Now I want to clone this function in C++. What should I do in the copy constructor of the function wrapper? I have...
Way too much detail missing for me to say anything useful.
Sorry, I'll be more verbose.
Start by defining your terms (or used established terminology):
What does it mean to have a "function written in Python derived from C++?" What does it mean to clone a function?
Following the Boost.Python tutorial, I have a abstract C++ base class, FunctionBase, with pure virtual functions. Derived from it is my wrapper class, FunctionWrap, which contains the implementation of the pure virtual functins of the base that uses call_method<> () (...) to call the Python function. I then implement a function in Python that derives from FunctionBase, FunctionWrap... export_FunctionBase () { class_ < FunctionBase, FunctionWrap, boost::noncopyable > ( "FunctionBase" )
FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap ) : FunctionBase ( wrap ), m_self ( wrap.m_self ) { // what should be here to create a new PyObject that is copy of old one?
What old one?
The object wrap.m_self which is pointer to PyObject in the constructor... FunctionWrap:: FunctionWrap ( PyObject * self ) : FunctionBase (), m_self ( self ) { } The C++ code makes a copy of the function by calling virtual method clone(), which is implemented as ... FunctionBase * FunctionWrap:: clone () const { return new FunctionWrap ( *this ); } So in the FunctionWrap copy constructor, I should make a copy of the Python * object held by the FunctionWrap object being copied, otherwise I get a shallow copy. FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap ) : FunctionBase ( wrap ) { m_self = ????(wrap.m_self)??? } Complete source code for FunctionWrap with its boost.python module use can be found here... http://www.slac.stanford.edu/grp/ek/hippodraw/FunctionWrap_8cxx-source.html
Paul F. Kunz wrote:
On Wed, 08 Dec 2004 14:44:52 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
I have a function written in Python derived from C++. Now I want to clone this function in C++. What should I do in the copy constructor of the function wrapper? I have...
Way too much detail missing for me to say anything useful.
Sorry, I'll be more verbose.
Start by defining your terms (or used established terminology):
What does it mean to have a "function written in Python derived from C++?" What does it mean to clone a function?
Following the Boost.Python tutorial, I have a abstract C++ base class, FunctionBase, with pure virtual functions. Derived from it is my wrapper class, FunctionWrap, which contains the implementation of the pure virtual functins of the base that uses call_method<> () (...) to call the Python function. I then implement a function in Python that derives from FunctionBase, FunctionWrap...
Functions in Python generally don't derive from anything. You implement a _class_ derived from FunctionBase?
export_FunctionBase () { class_ < FunctionBase, FunctionWrap, boost::noncopyable > ( "FunctionBase" )
Uhh, that's just the Python representation of FunctionBase, not anything derived from FunctionBase.
FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap ) : FunctionBase ( wrap ), m_self ( wrap.m_self ) { // what should be here to create a new PyObject that is copy of
old one? What you probably want is something like this: FunctionWrap:: FunctionWrap ( PyObject* self, const FunctionBase& f ) : FunctionBase ( f ), m_self ( self ) {} So you can write .def(init<FunctionBase const&>()) When wrapping FunctionBase. This all looks a lot simpler when you are using new-style polymorphism, though.
What old one?
The object wrap.m_self which is pointer to PyObject in the constructor...
FunctionWrap:: FunctionWrap ( PyObject * self ) : FunctionBase (), m_self ( self ) { }
The C++ code makes a copy of the function by calling virtual method clone(), which is implemented as ...
FunctionBase * FunctionWrap:: clone () const { return new FunctionWrap ( *this ); }
clone is a virtual function, right? Normally you should be wrapping it just like any other virtual function; something like: FunctionBase* FunctionWrap::clone() const { return call_method<FunctionBase*>(m_self, "clone"); } And then in Python you'd write: class MyFunction(FunctionBase): def clone(self): return MyFunction(self) But you're going to have a big ownership problem if this clone() is ever called from C++ because the new C++ FunctionWrap object will be owned by the newly-created Python MyFunction object. When call_method returns the last reference to that Python object will go away and destroy the C++ object so you'll be returning a dangling pointer. Okay, here's what I suggest, using new-style polymorphism, untested: template <class T> object get_owner(T* me) { // Use secret interface to get the Python object // that owns *this. I guess I will have to make that // interface public. return object(handle<>(borrowed(python::detail::wrapper_base_::get_owner(this)))); } class FunctionWrap : public FunctionBase, public wrapper<FunctionBase> { // expose this with def(init<FunctionBase const&>()) FunctionWrap(FunctionBase const& other) : FunctionBase(other) {} FunctionBase* clone() { object py_result; if (override clone = this->get_override("clone")) { // The Python class author overrode clone; do // whatever she says py_result = clone(); } else { object self = get_owner(this); // Find its most-derived Python class object my_class = self.attr("__class__"); // call the copy ctor through Python. py_result = my_class(self); } FunctionWrap* result = extract<FunctionBase*>(py_result); // Make the C++ result control the destiny of the Python result. result->invert_ownership = py_result; return result; } // Make sure that destroying the Python object doesn't cause an // attempt to destroy this one again. ~FunctionBase() { extract<std::auto_ptr<FunctionWrap>&> x(get_owner(this)); if (x) x().release(); } private: object invert_ownership; }; ... class_<std::auto_ptr<FunctionWrap> >("FunctionBase", init<FunctionBase const&>()) ... I hope this makes sense (and works!) And I hope that you'll make it into an instructive example that can be included in the tutorial. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
I'm stuck with the first function suggested by Dave. I have... template <class T> object FunctionWrap:: get_owner(T* me) { // Use secret interface to get the Python object // that owns *this. I guess I will have to make that // interface public. return object ( handle<> ( borrowed ( detail::wrapper_base_::get_owner(this)))); } and get... g++ -DQT_THREAD_SUPPORT -DHAVE_CONFIG_H -I. -I../../hippodraw/python -I.. -I../../hippodraw -I../../hippodraw/qt -I.. -I../qt -I/usr/local/include -I/usr/local/include/boost-1_32 -ftemplate-depth-120 -I/usr/local/include/python2.4 -I/usr/local/qt/include -g -O2 -Wall -c ../../hippodraw/python/FunctionWrap.cxx -MT FunctionWrap.lo -MD -MP -MF .deps/FunctionWrap.TPlo -fPIC -DPIC ../../hippodraw/python/FunctionWrap.cxx: In member function `boost::python::api::object FunctionWrap::get_owner(T*)': ../../hippodraw/python/FunctionWrap.cxx:86: error: invalid initialization of reference of type 'const volatile boost::python::detail::wrapper_base&' from expression of type 'FunctionWrap* const' /usr/local/include/boost-1_32/boost/python/detail/wrapper_base.hpp:72: error: in passing argument 1 of `PyObject* boost::python::detail::wrapper_base_::get_owner(const volatile boost::python::detail::wrapper_base&)' ../../hippodraw/python/FunctionWrap.cxx:86: error: no matching function for call to `borrowed(<type error>)' make[2]: *** [FunctionWrap.lo] Error 1 Compiler is gcc 3.4.3 on Red Hat 9 Linux. I've looked at the wrapper_base.hpp file, but don't understand it.
In response to my previsous item concerning compiler error. I found the problem. I needed `*this' instead of `this' in the argument.
On Wed, 08 Dec 2004 22:06:37 -0500, David Abrahams <dave@boost-consulting.com> said:
class_<std::auto_ptr<FunctionWrap> >("FunctionBase", init<FunctionBase const&>()) ...
I tried. Maybe I have typo I can't see. I have ... class_ < std::auto_ptr < FunctionWrap > > ( "FunctionBase", init < FunctionBase & > () ) and get ... ../../hippodraw/python/FunctionWrap.cxx:37: instantiated from here /usr/local/include/boost-1_32/boost/python/object/value_holder.hpp:133: error: no matching function for call to `std::auto_ptr<FunctionWrap>::auto_ptr(FunctionBase&)' /usr/local/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3/memory:351: note: candidates are: std::auto_ptr<_Tp>::auto_ptr(std::auto_ptr_ref<_Tp>) [with _Tp = FunctionWrap] /usr/local/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3/memory:200: note: std::auto_ptr<_Tp>::auto_ptr(std::auto_ptr<_Tp>&) [with _Tp = FunctionWrap] /usr/local/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3/memory:191: note: std::auto_ptr<_Tp>::auto_ptr(_Tp*) [with _Tp = FunctionWrap] where class FunctionWrap : public FunctionBase, public boost::python::wrapper < FunctionBase > { Any suggestions?
Paul F. Kunz wrote:
On Wed, 08 Dec 2004 22:06:37 -0500, David Abrahams <dave@boost-consulting.com> said:
class_<std::auto_ptr<FunctionWrap> >("FunctionBase", init<FunctionBase const&>()) ...
Sorry, this should be: class_<FunctionWrap, std::auto_ptr<FunctionWrap> >( "FunctionBase", init<FunctionBase const&>()) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Thu, 09 Dec 2004 20:37:18 -0500, David Abrahams <dave@boost-consulting.com> said:
Sorry, this should be:
class_<FunctionWrap, std::auto_ptr<FunctionWrap> >( "FunctionBase", init<FunctionBase const&>())
Ok, that compiles. Obvious no that I see it. But now at run time I get... Traceback (most recent call last): File "fitting.py", line 62, in ? f = Linear () File "fitting.py", line 21, in __init__ FunctionBase.__init__(self) Boost.Python.ArgumentError: Python argument types in FunctionBase.__init__(Linear) did not match C++ signature: __init__(_object*, FunctionBase)
from ... from hippo import FunctionBase class Linear ( FunctionBase ) : def __init__ ( self ) : FunctionBase.__init__(self) self.initialize () def initialize ( self ) : self.setName ( 'linear' ) self.setParmNames ( [ 'Intercept', 'Slope' ] ) self.setParameters ( [ intercept, slope ] ) f = Linear () ---
On Fri, 10 Dec 2004 07:38:12 -0800, "Paul F. Kunz" <Paul_Kunz@slac.stanford.edu> said:
On Thu, 09 Dec 2004 20:37:18 -0500, David Abrahams <dave@boost-consulting.com> said: Sorry, this should be:
class_<FunctionWrap, std::auto_ptr<FunctionWrap> >( "FunctionBase", init<FunctionBase const&>())
Ok, that compiles. Obvious no that I see it.
But now at run time I get...
Replying to my own item, I got pass the runtime error by doing the following instead of the above... class_ < FunctionBase, std::auto_ptr< FunctionWrap> > ( "FunctionBase" ) with the constructor of the class as ... FunctionWrap:: FunctionWrap ( PyObject * self ) : FunctionBase (), m_self ( self ) { } `m_self' is data member to hold the PyObject. Now at runtime I crash in the suggested FunctionWrap::clone() method when trying to call get_ower(). With gdb I see ... <boost::python::wrapper<FunctionBase>> = { <boost::python::detail::wrapper_base> = { m_self = 0x0 }, <No data fields>}, members of FunctionWrap: m_self = 0x4060911c, invert_ownership = { <boost::python::api::object_base> = { <boost::python::api::object_operators<boost::python::api::object>> = { <boost::python::def_visitor<boost::python::api::object>> = {<No data fields>}, <No data fields>}, members of boost::python::api::object_base: m_ptr = 0x8125d60 }, <No data fields>} } (gdb) Note that the wrapper_base data member `m_self' is a null pointer. So I guess my FunctionWrap class ... class FunctionWrap : public FunctionBase, public boost::python::wrapper < FunctionBase > { didn't get initialized properly. Looking at the wrapper.hpp and detail/wrapper_base.hpp files, I don't see how to initialize it. Do I really need it? If get_owner had worked, would it have returned FunctionWrap::m_self?
Paul F. Kunz wrote:
On Fri, 10 Dec 2004 07:38:12 -0800, "Paul F. Kunz" <Paul_Kunz@slac.stanford.edu> said:
On Thu, 09 Dec 2004 20:37:18 -0500, David Abrahams <dave@boost-consulting.com> said: Sorry, this should be:
class_<FunctionWrap, std::auto_ptr<FunctionWrap> >( "FunctionBase", init<FunctionBase const&>())
Ok, that compiles. Obvious no that I see it.
But now at run time I get...
Replying to my own item, I got pass the runtime error by doing the following instead of the above...
class_ < FunctionBase, std::auto_ptr< FunctionWrap> > ( "FunctionBase" )
Don't do that; the code I suggested was there for a reason. I think I have a bug, but it will help a lot to have a test case. Can you back up a step to the previous version that compiled and make a reduced test case for me to test my changes against? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Fri, 10 Dec 2004 14:45:02 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
> On Fri, 10 Dec 2004 07:38:12 -0800, "Paul F. Kunz" > <Paul_Kunz@slac.stanford.edu> said:
> On Thu, 09 Dec 2004 20:37:18 -0500, David Abrahams > <dave@boost-consulting.com> said: Sorry, this should be:
class_<FunctionWrap, std::auto_ptr<FunctionWrap> >( "FunctionBase", init<FunctionBase const&>())
Ok, that compiles. Obvious no that I see it.
But now at run time I get... Replying to my own item, I got pass the runtime error by doing the following instead of the above...
class_ < FunctionBase, std::auto_ptr< FunctionWrap> > ( "FunctionBase" )
Don't do that; the code I suggested was there for a reason.
Ok.
I think I have a bug, but it will help a lot to have a test case. Can you back up a step to the previous version that compiled and make a reduced test case for me to test my changes against?
I'll try. Frequently, it is hard to pull something out because large dependency on other parts of a large project. In this case, I think there's only one external class envolved.
On Fri, 10 Dec 2004 14:45:02 -0500, David Abrahams <dave@boost-consulting.com> said:
Don't do that; the code I suggested was there for a reason. I think I have a bug, but it will help a lot to have a test case. Can you back up a step to the previous version that compiled and make a reduced test case for me to test my changes against?
Ok at ... ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz with the necasary files and the test script. After building, just run the ``fitting.py' test script. There's a bit nore than needed in the tar ball, but is is sufficiently small.
Paul F. Kunz wrote:
On Fri, 10 Dec 2004 14:45:02 -0500, David Abrahams <dave@boost-consulting.com> said:
Don't do that; the code I suggested was there for a reason. I think I have a bug, but it will help a lot to have a test case. Can you back up a step to the previous version that compiled and make a reduced test case for me to test my changes against?
Ok at ...
ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz
with the necasary files and the test script. After building, just run the ``fitting.py' test script. There's a bit nore than needed in the tar ball, but is is sufficiently small.
Sufficiently for whom? It took me 20 minutes of massaging just to get it to build under vc7.1 (where I have good debugging tools). Then when I tested it against a Python debug build the vc7.1 runtime found a problem with corrupted dynamic memory. Please reduce your problem to a *simple* test case with as few classes and functions as possible. Take out any threading-related stuff. There should be one .cpp file and one .py file. Ideally, you'd pass me a Jamfile, but I can produce one myself if neccessary. Thanks, -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Sat, 11 Dec 2004 09:40:20 -0500, David Abrahams <dave@boost-consulting.com> said:
Sufficiently for whom? It took me 20 minutes of massaging just to get it to build under vc7.1 (where I have good debugging tools).
Sorry, I wasn't think about VC++
Please reduce your problem to a *simple* test case with as few classes and functions as possible. Take out any threading-related stuff. There should be one .cpp file and one .py file. Ideally, you'd pass me a Jamfile, but I can produce one myself if neccessary.
Ok, one file, one Python script. No includes other that C++ standard library. ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz Thanks for your patience.
Paul F. Kunz wrote:
On Sat, 11 Dec 2004 09:40:20 -0500, David Abrahams <dave@boost-consulting.com> said:
Sufficiently for whom? It took me 20 minutes of massaging just to get it to build under vc7.1 (where I have good debugging tools).
Sorry, I wasn't think about VC++
Please reduce your problem to a *simple* test case with as few classes and functions as possible. Take out any threading-related stuff. There should be one .cpp file and one .py file. Ideally, you'd pass me a Jamfile, but I can produce one myself if neccessary.
Ok, one file, one Python script. No includes other that C++ standard library.
ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz
I'm not surprised by the behavior of this example. You wrapped a class with a single constructor: class_ < FunctionWrap, std::auto_ptr < FunctionWrap > > ( "FunctionBase", init < const FunctionBase & > () ) ; That constructor takes a single FunctionBase& parameter. Leaving out the irrelevant Python code, you derive a class: from hippo import FunctionBase class Linear ( FunctionBase ) : def __init__ ( self ) : FunctionBase.__init__(self) But this is how you call a nullary (zero-argument) ctor on a base class! Don't try FunctionBase.__init__(self, self) it won't work. There's no C++ FunctionBase object yet that can be extracted from the 2nd argument. You just need to add a Default ctor to FunctionWrap and expose it. You also need to get rid of FunctionWrap(PyObject*) -- that's old-style polymorphism. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Sat, 11 Dec 2004 15:34:15 -0500, David Abrahams <dave@boost-consulting.com> said:
it won't work. There's no C++ FunctionBase object yet that can be extracted from the 2nd argument. You just need to add a Default ctor to FunctionWrap and expose it. You also need to get rid of FunctionWrap(PyObject*) -- that's old-style polymorphism.
Yes, that works. Thanks a million. Now back to trying to clone from C++.
This clone function doesn't quite work ... FunctionBase* clone() { PyGILState_STATE state = PyGILState_Ensure (); object py_result; if (override clone = this->get_override("clone")) { // The Python class author overrode clone; do // whatever she says py_result = clone(); } else { object self = get_owner(this); // Find its most-derived Python class object my_class = self.attr("__class__"); // call the copy ctor through Python. py_result = my_class(self); } FunctionWrap* result = extract<FunctionBase*>(py_result); // Make the C++ result control the destiny of the Python result. result->invert_ownership = py_result; PyGILState_Release ( state ); return result; } This is being called from different thread from the one that created the Python object, thus the Ensure/Release methods are used. If I implement clone in Python, the the function gets called, but calling clone() leads to ... terminate called after throwing an instance of 'boost::python::error_already_set' If I do not implement clone in Python, the the other path of the if is taken and get same result. I can call other methods implemented in Python without problem. With the debugger, I walked thru lots of Python code and get quite deep before it decides to return a null pointer instead of a new object. I note that it called, static PyObject * instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) Is this correct? In C++ an constructor wouldn't be called a instance method. Any suggestions on what to look for?
Paul F. Kunz wrote:
This clone function doesn't quite work ...
FunctionBase* clone() { PyGILState_STATE state = PyGILState_Ensure (); object py_result;
if (override clone = this->get_override("clone")) { // The Python class author overrode clone; do // whatever she says py_result = clone(); } else { object self = get_owner(this);
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self); } FunctionWrap* result = extract<FunctionBase*>(py_result);
// Make the C++ result control the destiny of the Python result. result->invert_ownership = py_result; PyGILState_Release ( state ); return result; }
This is being called from different thread from the one that created the Python object, thus the Ensure/Release methods are used.
If I implement clone in Python, the the function gets called, but calling clone() leads to ...
terminate called after throwing an instance of 'boost::python::error_already_set'
This means some Python exception is being thrown. You might set a breakpoint on boost::python::detail::throw_error_already_set (I think I have the namespace right) and look just up the stack to see what the actual error being reported is.
If I do not implement clone in Python, the the other path of the if is taken and get same result. I can call other methods implemented in Python without problem.
With the debugger, I walked thru lots of Python code and get quite deep before it
"it?"
decides to return a null pointer instead of a new object. I note that it called, static PyObject * instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) Is this correct?
How should I know? That function gets used to implement all sorts of things in Python.
In C++ an constructor wouldn't be called a instance method.
you are being led astray by terminology
Any suggestions on what to look for?
Yes: Get rid of all the threading stuff and *all* the superfluous functions/methods (you still had several unused Python methods floating around in what you posted the last time) and reduce this to a simple test case that can be used as a proof-of-concept for what you're doing with cloning. Test all the cloning cases you want to handle (e.g. override clone in some Python derived classes but not others). You will very quickly narrow the issue down and if you don't discover that you had a bug in your code, post the result and I will look at it. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
This means some Python exception is being thrown. You might set a breakpoint on boost::python::detail::throw_error_already_set (I think I have the namespace right) and look just up the stack to see what the actual error being reported is.
Doing that, I find exception being thrown from here... template <class T> inline T* expect_non_null(T* x) { if (x == 0) throw_error_already_set(); return x; } x is 0. This is result of calling... FunctionWrap * t = const_cast < FunctionWrap * > ( this ); object self = get_owner ( t ); // Find its most-derived Python class object my_class = self.attr("__class__"); // call the copy ctor through Python. py_result = my_class(self); This probably doesn't help, but thought I mentioned it in case it rings a bell.
Paul F. Kunz wrote:
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
This means some Python exception is being thrown. You might set a breakpoint on boost::python::detail::throw_error_already_set (I think I have the namespace right) and look just up the stack to see what the actual error being reported is.
Doing that, I find exception being thrown from here...
template <class T> inline T* expect_non_null(T* x) { if (x == 0) throw_error_already_set(); return x; }
x is 0. This is result of calling...
FunctionWrap * t = const_cast < FunctionWrap * > ( this ); object self = get_owner ( t );
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self);
Ahem. Which line above and precisely which part of the expression on that line is being evaluated?
This probably doesn't help, but thought I mentioned it in case it rings a bell.
No bell-ringing. The exception should be caught by Boost.Python and returned to Python as a Python exception. Python isn't issuing any error message? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Mon, 13 Dec 2004 14:06:26 -0500, David Abrahams <dave@boost-consulting.com> said:
FunctionWrap * t = const_cast < FunctionWrap * > ( this ); object self = get_owner ( t );
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self);
Ahem. Which line above and precisely which part of the expression on that line is being evaluated?
The last line, my_class(self)
This probably doesn't help, but thought I mentioned it in case it rings a bell.
No bell-ringing. The exception should be caught by Boost.Python and returned to Python as a Python exception. Python isn't issuing any error message?
No error message from Python. Just silently returns a NULL result. P.S. I am trying to find a way to isolate the problem.
Paul F. Kunz wrote:
On Mon, 13 Dec 2004 14:06:26 -0500, David Abrahams <dave@boost-consulting.com> said:
FunctionWrap * t = const_cast < FunctionWrap * > ( this ); object self = get_owner ( t );
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self);
Ahem. Which line above and precisely which part of the expression on that line is being evaluated?
The last line, my_class(self)
This probably doesn't help, but thought I mentioned it in case it rings a bell.
No bell-ringing. The exception should be caught by Boost.Python and returned to Python as a Python exception. Python isn't issuing any error message?
No error message from Python. Just silently returns a NULL result.
The error message only appears on your screen *after* the exception is thrown by C++ in response to the NULL result, and caught. Somewhere up the call stack is boost::python::handle_exception, which catches the C++ exception and returns an error code to the Python interpreter, which should eventually issue an error message (unless of course your Python code is eating the resulting Python exception).
P.S.
I am trying to find a way to isolate the problem.
Good. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
This means some Python exception is being thrown. You might set a breakpoint on boost::python::detail::throw_error_already_set (I think I have the namespace right) and look just up the stack to see what the actual error being reported is.
It appears that Python is creating the null pointer in type_call() after these lines... type = obj->ob_type; if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL && type->tp_init(obj, args, kwds) < 0) { Py_DECREF(obj); obj = NULL;
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
This means some Python exception is being thrown. You might set a breakpoint on boost::python::detail::throw_error_already_set (I think I have the namespace right) and look just up the stack to see what the actual error being reported is.
Here's the what type contains in type = obj->ob_type; if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL && type->tp_init(obj, args, kwds) < 0) { Py_DECREF(obj); obj = NULL; (gdb) p *obj->ob_type $10 = { ob_refcnt = 5, ob_type = 0x41ce55c0, ob_size = 0, tp_name = 0x401941b4 "Linear", tp_basicsize = 24, tp_itemsize = 1, tp_dealloc = 0x808cab0 <subtype_dealloc>, tp_print = 0, tp_getattr = 0, tp_setattr = 0, tp_compare = 0, tp_repr = 0x808d910 <object_repr>, tp_as_number = 0x819e28c, tp_as_sequence = 0x819e330, tp_as_mapping = 0x819e324, tp_hash = 0x808dbc0 <object_hash>, tp_call = 0, tp_str = 0x808db90 <object_str>, tp_getattro = 0x8080940 <PyObject_GenericGetAttr>, tp_setattro = 0x8080c00 <PyObject_GenericSetAttr>, tp_as_buffer = 0x819e358, tp_flags = 22523, tp_doc = 0x0, tp_traverse = 0x808c8c0 <subtype_traverse>, tp_clear = 0x808ca00 <subtype_clear>, tp_richcompare = 0, tp_weaklistoffset = 16, tp_iter = 0, tp_iternext = 0, tp_methods = 0x0, tp_members = 0x819e370, tp_getset = 0x0, tp_base = 0x862f4e4, tp_dict = 0x4060835c, tp_descr_get = 0, tp_descr_set = 0, tp_dictoffset = 12, tp_init = 0x80949a0 <slot_tp_init>, tp_alloc = 0x808c7a0 <PyType_GenericAlloc>, tp_new = 0x41cc9ca0 <boost::python::instance_holder::install(_object*)+32>, tp_free = 0x80eb2c0 <PyObject_GC_Del>, tp_is_gc = 0, tp_bases = 0x401a3ecc, tp_mro = 0x4019320c, tp_cache = 0x0, tp_subclasses = 0x0, tp_weaklist = 0x40600edc, tp_del = 0 } I don't know what is good or bad in the above. Maybe someone does.
I decided to try invoking this clone function from Python instead of from C++ so as to try to isolate the problem (no threads, etc). So I just added
foo = f.clone()
to the test script after exposing this method to Python and get ... Traceback (most recent call last): File "fitting.py", line 66, in ? foo = f.clone () TypeError: __init__() takes exactly 1 argument (2 given) This might be the error message that we're not getting when clone() is called from C++. Maybe we're not getting the message because boost::python::throw_error_already_set () is calling std::terminate?
I replaced the file ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz With one that illustrates the clone() problem. One file, one script, no threads.
Paul F. Kunz wrote:
I replaced the file
ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz
With one that illustrates the clone() problem. One file, one script, no threads.
Paul, I've take a look, but you really could make my life easier. I don't need makefiles or (worse) autoconf ".in" files. I don't need subdirectories. I just want a .cpp (not .cxx, if we're going to pick nits) and a .py file. You could strip out all the extraneous macros too. Traceback (most recent call last): File "c:\boost\libs\python\user\hippo/fitting.py", line 32, in ? f.test() TypeError: __init__() takes exactly 1 argument (2 given) [5611 refs] So your problem is really simple. In cloning the Linear object you've created, its __init__ method is getting called with *another* Linear object that should be the source of the clone operation. But your Linear's __init__ function only takes one argument, as the error message says. If you want to pass through both default construction and copy construction, you have to write something like: def __init__ ( self, other = None ): if other: FunctionBase.__init__(self, other) else: FunctionBase.__init__(self) But then, this is boring; why don't you leave off the __init__ function altogether? Then the one(s) supplied by FunctionBase will get called. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Tue, 14 Dec 2004 15:40:10 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
I replaced the file
ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz
With one that illustrates the clone() problem. One file, one script, no threads.
Paul, I've take a look, but you really could make my life easier. I don't need makefiles or (worse) autoconf ".in" files. I don't need subdirectories. I just want a .cpp (not .cxx, if we're going to pick nits) and a .py file. You could strip out all the extraneous macros too.
I think I solved that problem, see item I posted after this one.
Paul F. Kunz wrote:
On Tue, 14 Dec 2004 15:40:10 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
I replaced the file
ftp://ftp.slac.stanford.edu/users/pfkeb/abrahams/HippoDraw-1.12.7.tar.gz
With one that illustrates the clone() problem. One file, one script, no threads.
Paul, I've take a look, but you really could make my life easier. I don't need makefiles or (worse) autoconf ".in" files. I don't need subdirectories. I just want a .cpp (not .cxx, if we're going to pick nits) and a .py file. You could strip out all the extraneous macros too.
I think I solved that problem, see item I posted after this one.
No, your "solution" was in error. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
This clone function doesn't quite work ...
FunctionBase* clone() { PyGILState_STATE state = PyGILState_Ensure (); object py_result;
if (override clone = this->get_override("clone")) { // The Python class author overrode clone; do // whatever she says py_result = clone(); } else { object self = get_owner(this);
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self);
The line above was the problem It should be py_result = my_class(); i.e., no arguments. Thanks Dave for your help and patience with me.
Paul F. Kunz wrote:
On Sun, 12 Dec 2004 22:26:21 -0500, David Abrahams <dave@boost-consulting.com> said:
Paul F. Kunz wrote:
This clone function doesn't quite work ...
FunctionBase* clone() { PyGILState_STATE state = PyGILState_Ensure (); object py_result;
if (override clone = this->get_override("clone")) { // The Python class author overrode clone; do // whatever she says py_result = clone(); } else { object self = get_owner(this);
// Find its most-derived Python class object my_class = self.attr("__class__");
// call the copy ctor through Python. py_result = my_class(self);
The line above was the problem It should be
py_result = my_class();
i.e., no arguments.
No, that's wrong. That line is supposed to call the exposed copy ctor. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
On Tue, 14 Dec 2004 15:41:02 -0500, David Abrahams <dave@boost-consulting.com> said:
py_result = my_class();
i.e., no arguments.
No, that's wrong. That line is supposed to call the exposed copy ctor.
I restored py_result = my_class ( self ) and exposed the copy constructor as you suggested. This works. Thanks again, Dave.
Some time ago, I got this work under Linux with both gcc 2.95.3 and gcc 3.4.3. Now I'm testing under Windows with VC++ 7.1 and having trouble. The first part of the wrapping code is... using namespace boost::python; namespace hippodraw { namespace Python { void export_FunctionBase () { class_ < FunctionWrap, std::auto_ptr < FunctionWrap> > ( "FunctionBase", init < const FunctionBase & > () ) // line with error .def ( init < const FunctionWrap & > () ) .def ( init <> () ) .def ( "initialize", &FunctionWrap::initialize ) .def ( "name", &FunctionBase::name, return_value_policy < copy_const_reference > () ) .def ( "setName", &FunctionWrap::setName ) The VC++ build log is attached below and is saying can't instanciate FunctionBase because pure virtual functions have no definition. These functions are defined in FunctionWrap. Is this possibly a problem with VC++ or is gcc letting me get away with something. Build Log ------- Build started: Project: pyhippo, Configuration: ReleaseAll|Win32 ------- Command Lines Creating temporary file "c:\hippodraw\vs.net2003\pyhippo\ReleaseAll\RSP000012.rsp" with contents [ /O2 /I "..\qthippo" /I "..\..\qt" /I "..\.." /I "C:\Qt\3.3.3\include" /I "C:\Boost\include\boost-1_32" /I "c:\Python24\include" /I "C:\Minuit" /I "C:\cfitsio" /D "WIN32" /D "NDEBUG" /D "QT_THREAD_SUPPORT" /D "HAVE_MINUIT" /D "HAVE_ROOT" /D "MDL_HIPPOPLOT_IMPORTS" /D "_WINDLL" /D "_MBCS" /FD /EHsc /MD /GR /Fo"ReleaseAll/" /Fd"ReleaseAll/vc70.pdb" /W3 /c /TP /wd4251 \hippodraw\python\ObserverWrap.cxx ] Creating command line "cl.exe @c:\hippodraw\vs.net2003\pyhippo\ReleaseAll\RSP000012.rsp /nologo" Creating temporary file "c:\hippodraw\vs.net2003\pyhippo\ReleaseAll\RSP000013.rsp" with contents [ /O2 /I "..\qthippo" /I "..\..\qt" /I "..\.." /I "C:\Qt\3.3.3\include" /I "C:\Boost\include\boost-1_32" /I "c:\Python24\include" /I "C:\Minuit" /I "C:\cfitsio" /D "WIN32" /D "NDEBUG" /D "QT_THREAD_SUPPORT" /D "HAVE_MINUIT" /D "HAVE_ROOT" /D "MDL_HIPPOPLOT_IMPORTS" /D "_WINDLL" /D "_MBCS" /FD /EHsc /MD /GR /Fo"ReleaseAll/" /Fd"ReleaseAll/vc70.pdb" /W3 /c /TP /wd4251 \hippodraw\python\FunctionWrap.cxx ] Creating command line "cl.exe @c:\hippodraw\vs.net2003\pyhippo\ReleaseAll\RSP000013.rsp /nologo" Output Window Compiling... ObserverWrap.cxx Compiling... FunctionWrap.cxx C:\Boost\include\boost-1_32\boost\python\converter\as_to_python_function.hpp(21) : error C2259: 'FunctionBase' : cannot instantiate abstract class due to following members: 'double FunctionBase::derivByParm(int,double) const' : pure virtual function was not defined ..\..\functions\FunctionBase.h(134) : see declaration of 'FunctionBase::derivByParm' 'void FunctionBase::initialize(void)' : pure virtual function was not defined ..\..\functions\FunctionBase.h(165) : see declaration of 'FunctionBase::initialize' 'double FunctionBase::operator ()(double) const' : pure virtual function was not defined ..\..\functions\FunctionBase.h(254) : see declaration of 'FunctionBase::operator`()'' C:\Boost\include\boost-1_32\boost\python\to_python_converter.hpp(34) : see reference to class template instantiation 'boost::python::converter::as_to_python_function' being compiled with [ T=FunctionBase, ToPython=boost::python::objects::class_cref_wrapper,boost::python::detail::not_specified,boost::python::detail::not_specified>::holder>> ] C:\Boost\include\boost-1_32\boost\detail\compressed_pair.hpp(200) : while compiling class-template member function 'boost::python::to_python_converter::to_python_converter(void)' with [ T=FunctionBase, Conversion=boost::python::objects::class_cref_wrapper,boost::python::detail::not_specified,boost::python::detail::not_specified>::holder>> ] C:\Boost\include\boost-1_32\boost\python\object\class_wrapper.hpp(23) : see reference to class template instantiation 'boost::python::to_python_converter' being compiled with [ T=FunctionBase, Conversion=boost::python::objects::class_cref_wrapper,boost::python::detail::not_specified,boost::python::detail::not_specified>::holder>> ] C:\Boost\include\boost-1_32\boost\python\object\class_metadata.hpp(248) : see reference to class template instantiation 'boost::python::objects::class_cref_wrapper' being compiled with [ Src=FunctionBase, MakeInstance=boost::python::objects::make_instance,boost::python::detail::not_specified,boost::python::detail::not_specified>::holder> ] C:\Boost\include\boost-1_32\boost\python\object\class_metadata.hpp(218) : see reference to function template instantiation 'void boost::python::objects::class_metadata::maybe_register_class_to_python(T2 *,boost::mpl::false_)' being compiled with [ T=FunctionWrap, X1=std::auto_ptr, X2=boost::python::detail::not_specified, X3=boost::python::detail::not_specified, T2=FunctionBase ] C:\Boost\include\boost-1_32\boost\python\object\class_metadata.hpp(202) : see reference to function template instantiation 'void boost::python::objects::class_metadata::register_aux2>(T2 *,Callback)' being compiled with [ T=FunctionWrap, X1=std::auto_ptr, X2=boost::python::detail::not_specified, X3=boost::python::detail::not_specified, T2=FunctionBase, C_=true, Callback=boost::mpl::bool_ ] C:\Boost\include\boost-1_32\boost\python\object\class_metadata.hpp(195) : see reference to function template instantiation 'void boost::python::objects::class_metadata::register_aux(boost::python::wrapper *)' being compiled with [ T=FunctionWrap, X1=std::auto_ptr, X2=boost::python::detail::not_specified, X3=boost::python::detail::not_specified ] C:\Boost\include\boost-1_32\boost\python\object\class_metadata.hpp(194) : while compiling class-template member function 'void boost::python::objects::class_metadata::register_(void)' with [ T=FunctionWrap, X1=std::auto_ptr, X2=boost::python::detail::not_specified, X3=boost::python::detail::not_specified ] C:\Boost\include\boost-1_32\boost\python\class.hpp(174) : see reference to class template instantiation 'boost::python::objects::class_metadata' being compiled with [ T=FunctionWrap, X1=std::auto_ptr, X2=boost::python::detail::not_specified, X3=boost::python::detail::not_specified ] C:\Boost\include\boost-1_32\boost\python\class.hpp(205) : see reference to class template instantiation 'boost::python::class_::id_vector' being compiled with [ W=FunctionWrap, X1=std::auto_ptr ] \hippodraw\python\FunctionWrap.cxx(27) : see reference to function template instantiation 'boost::python::class_::class_>(const char *,const boost::python::init_base &)' being compiled with [ W=FunctionWrap, X1=std::auto_ptr, T0=const FunctionBase &, DerivedT=boost::python::init ] Results Build log was saved at "file://c:\hippodraw\vs.net2003\pyhippo\ReleaseAll\BuildLog.htm" pyhippo - 1 error(s), 0 warning(s) Complete source code can be browsed here ... http://www.slac.stanford.edu/grp/ek/hippodraw
"Paul F. Kunz" <Paul_Kunz@slac.stanford.edu> writes:
Some time ago, I got this work under Linux with both gcc 2.95.3 and gcc 3.4.3. Now I'm testing under Windows with VC++ 7.1 and having trouble.
The first part of the wrapping code is...
using namespace boost::python;
namespace hippodraw { namespace Python { void export_FunctionBase () { class_ < FunctionWrap, std::auto_ptr < FunctionWrap> > ( "FunctionBase", init < const FunctionBase & > () ) // line with error .def ( init < const FunctionWrap & > () ) .def ( init <> () ) .def ( "initialize", &FunctionWrap::initialize ) .def ( "name", &FunctionBase::name, return_value_policy < copy_const_reference > () ) .def ( "setName", &FunctionWrap::setName )
The VC++ build log is attached below
Was it filtered by something? I can't see much of the information I'm used to getting from VC++ there.
and is saying can't instanciate FunctionBase because pure virtual functions have no definition. These functions are defined in FunctionWrap. Is this possibly a problem with VC++ or is gcc letting me get away with something.
Looks like a VC++ problem, though I don't know for sure. Is FunctionWrap copyable? Otherwise you should use noncopyable in its class_ and these errors might disappear. -- Dave Abrahams Boost Consulting www.boost-consulting.com
On Tue, 22 Mar 2005 17:57:49 -0500, David Abrahams <dave@boost-consulting.com> said:
The VC++ build log is attached below
Was it filtered by something? I can't see much of the information I'm used to getting from VC++ there.
No, just converted from html to plain text.
and is saying can't instanciate FunctionBase because pure virtual functions have no definition. These functions are defined in FunctionWrap. Is this possibly a problem with VC++ or is gcc letting me get away with something.
Looks like a VC++ problem, though I don't know for sure. Is FunctionWrap copyable?
Didn't we have to make it copyable so that clone member function could be implemented in Python?
Otherwise you should use noncopyable in its class_ and these errors might disappear.
I'll try, but not right away.
"Paul F. Kunz" <Paul_Kunz@slac.stanford.edu> writes:
On Tue, 22 Mar 2005 17:57:49 -0500, David Abrahams <dave@boost-consulting.com> said:
The VC++ build log is attached below
Was it filtered by something? I can't see much of the information I'm used to getting from VC++ there.
No, just converted from html to plain text.
Try building from the command-line; you'll see more.
and is saying can't instanciate FunctionBase because pure virtual functions have no definition. These functions are defined in FunctionWrap. Is this possibly a problem with VC++ or is gcc letting me get away with something.
Looks like a VC++ problem, though I don't know for sure. Is FunctionWrap copyable?
Didn't we have to make it copyable so that clone member function could be implemented in Python?
I don't know what you had to do, and if I helped you with this code in the past I don't remember it. -- Dave Abrahams Boost Consulting www.boost-consulting.com
I'm having problem with destructor suggested by Dave FunctionWrap:: ~FunctionWrap () { cout << "FunctionWrap::destructor" << endl; extract<std::auto_ptr<FunctionWrap>&> x(get_owner(this)); if ( x.check() ) x().release(); } When I quit python, it appears to be called recursively before things die on seg fault.
participants (2)
-
David Abrahams -
Paul F. Kunz