#2049 closed Bugs (invalid)
extracting c++ pointer from python object with multiple parents
Reported by: | Owned by: | Dave Abrahams | |
---|---|---|---|
Milestone: | Component: | python USE GITHUB | |
Version: | Boost 1.35.0 | Severity: | Problem |
Keywords: | Cc: | Stefan Seefeld |
Description
Extracting a C++ pointer from a python object which inherited two base classes form C++ doesn't work correctly. Only the first base can be extracted, the second cannot be extracted under all circumstances. The script runs within an embedded python interpreter.
CPP code:
#define BOOST_PYTHON_STATIC_LIB #include <boost/python.hpp> using namespace boost::python; class Base1 { public: virtual ~Base1() {} }; class Base2 { public: virtual ~Base2() {} }; void extractBase1( boost::python::object& obj) { Base1* ex1 = extract<Base1*>(obj) BOOST_EXTRACT_WORKAROUND; Base1& ex2 = extract<Base1&>(obj) BOOST_EXTRACT_WORKAROUND; } void extractBase2( boost::python::object& obj) { Base2* ex1 = extract<Base2*>(obj) BOOST_EXTRACT_WORKAROUND; Base2& ex2 = extract<Base2&>(obj) BOOST_EXTRACT_WORKAROUND; } BOOST_PYTHON_MODULE(Test_ext) { class_<Base1, boost::noncopyable>("Base1"); class_<Base2, boost::noncopyable>("Base2"); def("extractBase1", extractBase1); def("extractBase2", extractBase2); } int main(int, char **) { try { Py_Initialize(); initTest_ext(); object main = import("__main__"); object dictionary(main.attr("__dict__")); object result = exec_file("test.py", dictionary, dictionary ); } catch (error_already_set) { PyErr_Print(); } return 0; }
test.py:
from Test_ext import * class both1(Base1, Base2): def foo(self): print "foo!" class both2(Base2, Base1): def foo(self): print "foo2!" obj1 = both1() extractBase1(obj1) extractBase2(obj1) # doesn't work obj2 = both() extractBase1(obj2) # doesn't work extractBase2(obj2)
Change History (7)
follow-up: 2 comment:1 by , 14 years ago
follow-up: 3 comment:2 by , 14 years ago
Replying to dave:
Does this fail even when the dtors are non-virtual?
yes it does, with the same error as always:
Traceback (most recent call last): File "test.py", line 15, in <module> extractBase2(obj1) # doesn't work TypeError: No registered converter was able to extract a C++ pointer to type class Base2 from this Python object of type both1
follow-up: 4 comment:3 by , 14 years ago
Cc: | added |
---|---|
Resolution: | → invalid |
Status: | new → closed |
Replying to bloodyfanatic@gmx.de:
Replying to dave:
Does this fail even when the dtors are non-virtual?
yes it does, with the same error as always:
In that case, please post the fully reduced test case
Also, you need an init method in both1 and both2 that initializes *both* bases or this can never work, because a wrapped class' init function is what actually constructs the C++ base object. Without it, there is no base object to which a pointer can be returned from extract
:
def __init__(self): Base1.__init__(self) Base2.__init__(self)
I don't know how to do this properly with super (the technique Stefan was suggesting), but his suggestion as written didn't take care of both bases, as the following demonstrates:
>>> class X(object): ... def __init__(self): ... print 'X' ... >>> class Y(object): ... def __init__(self): ... print 'Y' ... >>> class Z(X,Y): pass # essentially what you posted ... >>> z = Z() X >>> class Z(X,Y): ... def __init__(self): ... super(Z,self).__init__() # Stefan's suggestion ... >>> z = Z() X >>> class Z(X,Y): ... def __init__(self): ... X.__init__(self) ... Y.__init__(self) ... >>> z = Z() X Y >>>
I'm pretty sure this solves your problem so I'm closing the ticket, but if it doesn't, please re-open it.
comment:4 by , 14 years ago
Replying to dave:
Replying to bloodyfanatic@gmx.de:
Replying to dave:
Does this fail even when the dtors are non-virtual?
yes it does, with the same error as always:
In that case, please post the fully reduced test case
Also, you need an init method in both1 and both2 that initializes *both* bases or this can never work, because a wrapped class' init function is what actually constructs the C++ base object. Without it, there is no base object to which a pointer can be returned from
extract
:def __init__(self): Base1.__init__(self) Base2.__init__(self)I don't know how to do this properly with super (the technique Stefan was suggesting), but his suggestion as written didn't take care of both bases, as the following demonstrates:
>>> class X(object): ... def __init__(self): ... print 'X' ... >>> class Y(object): ... def __init__(self): ... print 'Y' ... >>> class Z(X,Y): pass # essentially what you posted ... >>> z = Z() X >>> class Z(X,Y): ... def __init__(self): ... super(Z,self).__init__() # Stefan's suggestion ... >>> z = Z() X >>> class Z(X,Y): ... def __init__(self): ... X.__init__(self) ... Y.__init__(self) ... >>> z = Z() X Y >>>I'm pretty sure this solves your problem so I'm closing the ticket, but if it doesn't, please re-open it.
just tested that and it works. Thank you very much :)
comment:5 by , 14 years ago
Dave,
The use of 'super' with init does in fact work, as long as all classes use it:
class A(object): def __init__(self): super(A, self).__init__() print 'A' class B(object): def __init__(self): super(B, self).__init__() print 'B' class C(A, B): def __init__(self): super(C, self).__init__() print 'C' c = C() # prints A B C
The reason for this is pretty well explained in http://fuhm.net/super-harmful/. In a nutshell: super() doesn't (necessarily) invoke the superclass method itself, but the next one in the MRO chain. If you omit super in one case, you break out of this chain.
FWIW.
Does this fail even when the dtors are non-virtual?