Problems with dynamic casts
Hi, I'm having a problem with dynamic casting on an object constructed using Python. It seems as though the type of an owned object is being lost. I have a test as follows: assert (dynamic_cast<element::Comments *>(loc.getElement(elists[0])) !=NULL); When I invoke it from the constructor of the owning object (the object that owns elists), the test passes. But when I construct the owning object and then invoke the test through Python, it fails. I'm using gcc 3.0.4 from Debian stable. This looks ugly to me. So far the only explanation I've been able to come up with is memory corruption. Aaron -- Aaron Bentley Director of Technology Panometrics, Inc.
Aaron Bentley <abentley@panoramicfeedback.com> writes:
Hi, I'm having a problem with dynamic casting on an object constructed using Python. It seems as though the type of an owned object is being lost.
I have a test as follows:
assert (dynamic_cast<element::Comments *>(loc.getElement(elists[0])) !=NULL);
When I invoke it from the constructor of the owning object (the object that owns elists), the test passes. But when I construct the owning object and then invoke the test through Python, it fails.
I'm using gcc 3.0.4 from Debian stable.
This looks ugly to me. So far the only explanation I've been able to come up with is memory corruption.
I suggest you reduce your problem to the smallest possible example that illustrates it. The answer will probably become obvious to you, but if it doesn't, please post your example here. -- Dave Abrahams Boost Consulting www.boost-consulting.com
David Abrahams wrote:
Aaron Bentley <abentley@panoramicfeedback.com> writes:
Hi, I'm having a problem with dynamic casting on an object constructed using Python. It seems as though the type of an owned object is being lost.
I suggest you reduce your problem to the smallest possible example that illustrates it. The answer will probably become obvious to you, but if it doesn't, please post your example here.
Okay, here's some stripped down examples: ---foo.h--- class BaseClass { public: BaseClass() {} virtual ~BaseClass() {} }; ---foo.cpp--- ... SubClass *subclass_cast(BaseClass *el) { return dynamic_cast<SubClass *>(el); } ---bar.h--- class SubClass: public BaseClass { public: SubClass(); }; SubClass *subclass_cast(BaseClass *el); void test_subclass(SubClass &el); ---bar.cpp--- ... SubClass::SubClass() { } void test_subclass(SubClass &el) { ; } --wrapper.cpp-- ... BaseClass *return_base(SubClass *el) { return el; } ... BOOST_PYTHON_MODULE(_element) { class_<BaseClass, std::auto_ptr<BaseClass>, boost::noncopyable>( "BaseClass", no_init) ; class_<SubClass, std::auto_ptr<SubClass>, bases<BaseClass>, boost::noncopyable>("SubClass") ; def ("subclass_cast", subclass_cast, return_internal_reference<1>()); def ("test_subclass", test_subclass); def ("return_base", return_base, return_internal_reference<1>()); ... } --- test.py --- import _element def run_test(test_func, converter, caster, subclass): print "Testing %s" % subclass.__name__ subcl = subclass() try: test_func(subcl) print "subcl can be passed to %s directly" % test_func.__name__ except Exception, e: print print e print "subcl cannot be passed to %s directly:" % test_func.__name__ try: test_func(converter(subcl)) print "subcl can be passed to %s when returned as a base class" % \ test_func.__name__ except Exception, e: print print e print "subcl cannot be passed to %s when returned as a base class" % \ test_func.__name__ try: assert caster((subcl)) is not None print "subcl can be dynamically cast from subclass" except Exception, e: print if len(str(e)) > 0: print e print e.__class__ print "subcl cannot be dynamically cast from subclass" try: assert caster(converter(subcl)) is not None print "subcl can be dynamically cast from base class" except Exception, e: print if len(str(e)) > 0: print e print e.__class__ print "subcl cannot be dynamically cast from base class" run_test(_element.test_subclass, _element.return_base, _element.subclass_cast, _element.SubClass) Everything except wrapper.cpp is built as one shared library. When I run test.py, I get this: Testing SubClass subcl can be passed to test_subclass directly Python argument types in pflib._element.test_subclass(SubClass) did not match C++ signature: test_subclass(8SubClass {lvalue}) subcl cannot be passed to test_subclass when returned as a base class subcl can be dynamically cast from subclass subcl can be dynamically cast from base class If I change the constructor for SubClass to an inline constructor, I get this: Testing SubClass subcl can be passed to test_subclass directly subcl can be passed to test_subclass when returned as a base class exceptions.AssertionError subcl cannot be dynamically cast from subclass exceptions.AssertionError subcl cannot be dynamically cast from base class So it seems I can't do implicit dynamic casts (passing a base-class pointer to a function taking a subclass pointer) and explicit dynamic casts at the same time. Aaron -- Aaron Bentley Director of Technology Panometrics, Inc.
Does anyone have any suggestions for dealing with these issues? Aaron Aaron Bentley wrote:
David Abrahams wrote:
Aaron Bentley <abentley@panoramicfeedback.com> writes:
Hi, I'm having a problem with dynamic casting on an object constructed using Python. It seems as though the type of an owned object is being lost.
I suggest you reduce your problem to the smallest possible example that illustrates it. The answer will probably become obvious to you, but if it doesn't, please post your example here.
Okay, here's some stripped down examples:
---foo.h--- class BaseClass { public: BaseClass() {} virtual ~BaseClass() {} };
---foo.cpp--- ... SubClass *subclass_cast(BaseClass *el) { return dynamic_cast<SubClass *>(el); } ---bar.h--- class SubClass: public BaseClass { public: SubClass(); }; SubClass *subclass_cast(BaseClass *el);
void test_subclass(SubClass &el); ---bar.cpp--- ... SubClass::SubClass() { } void test_subclass(SubClass &el) { ; } --wrapper.cpp-- ... BaseClass *return_base(SubClass *el) { return el; } ... BOOST_PYTHON_MODULE(_element) { class_<BaseClass, std::auto_ptr<BaseClass>, boost::noncopyable>( "BaseClass", no_init) ; class_<SubClass, std::auto_ptr<SubClass>, bases<BaseClass>, boost::noncopyable>("SubClass") ; def ("subclass_cast", subclass_cast, return_internal_reference<1>()); def ("test_subclass", test_subclass); def ("return_base", return_base, return_internal_reference<1>());
... } --- test.py --- import _element def run_test(test_func, converter, caster, subclass): print "Testing %s" % subclass.__name__ subcl = subclass() try: test_func(subcl) print "subcl can be passed to %s directly" % test_func.__name__ except Exception, e: print print e print "subcl cannot be passed to %s directly:" % test_func.__name__ try: test_func(converter(subcl)) print "subcl can be passed to %s when returned as a base class" % \ test_func.__name__ except Exception, e: print print e print "subcl cannot be passed to %s when returned as a base class" % \ test_func.__name__ try: assert caster((subcl)) is not None print "subcl can be dynamically cast from subclass" except Exception, e: print if len(str(e)) > 0: print e print e.__class__ print "subcl cannot be dynamically cast from subclass" try: assert caster(converter(subcl)) is not None print "subcl can be dynamically cast from base class" except Exception, e: print if len(str(e)) > 0: print e print e.__class__ print "subcl cannot be dynamically cast from base class" run_test(_element.test_subclass, _element.return_base, _element.subclass_cast, _element.SubClass)
Everything except wrapper.cpp is built as one shared library.
When I run test.py, I get this: Testing SubClass subcl can be passed to test_subclass directly
Python argument types in pflib._element.test_subclass(SubClass) did not match C++ signature: test_subclass(8SubClass {lvalue}) subcl cannot be passed to test_subclass when returned as a base class subcl can be dynamically cast from subclass subcl can be dynamically cast from base class
If I change the constructor for SubClass to an inline constructor, I get this: Testing SubClass subcl can be passed to test_subclass directly subcl can be passed to test_subclass when returned as a base class
exceptions.AssertionError subcl cannot be dynamically cast from subclass
exceptions.AssertionError subcl cannot be dynamically cast from base class
So it seems I can't do implicit dynamic casts (passing a base-class pointer to a function taking a subclass pointer) and explicit dynamic casts at the same time.
Aaron
-- Aaron Bentley Director of Technology Panometrics, Inc.
On Fri, 18 Mar 2005 11:03:28 -0500, Aaron Bentley <abentley@panoramicfeedback.com> wrote:
Does anyone have any suggestions for dealing with these issues?
Aaron
I'm having what looks to be the same problem. I *think* that the meat of the issue is addressed here: http://www.python.org/moin/boost.python/CrossExtensionModuleDependencies So then it becomes a matter of controlling where the compiler (I assume you're also using gcc 3?) emits vtables. I've tried (more or less blindly) several different methods to get that information into a common third shared library that gets loaded first, but nothing I've tried seems to solve the problem. While searching for information on this, I stumbled across some posts by Dave Abrahams on the gcc mailing list regarding this issue (or so it seems), but the technical details were pretty much over my head. I got the impression that the current method used for resolving weak symbols is not exactly ideal for our purposes, but I'm still not sure about the status of a decent workaround. -- Dan Haffey
I'm having what looks to be the same problem.
Please disregard that message, it turns out that in my case, PEBKAC (been too long since I'd RTFM for Pyste wrt inheritance). For what it's worth, I compiled your example and it ran fine for me (with gcc 3.3.4). I notice that the class name in part of your exception text is preceded by an 8, which looks like part of the mangled name. Whenever I get the "did not match C++ signature" error, the class name matches up. Maybe your compiler is playing funny games? -- Dan Haffey
Aaron Bentley <abentley@panoramicfeedback.com> writes:
Okay, here's some stripped down examples:
---foo.h--- class BaseClass { public: BaseClass() {} virtual ~BaseClass() {} };
---foo.cpp--- ... ^^^ That's not very useful; it produces a syntax error. Please post something that compiles.
<snip>
Everything except wrapper.cpp is built as one shared library.
And how is wrapper.cpp built? How about just posting a Jamfile?
When I run test.py, I get this: Testing SubClass
<snip>
If I change the constructor for SubClass to an inline constructor
I can tell you that in many compilers, RTTI information typically gets generated where a class' first non-inline function is defined.
, I get this:
<snip> How about reducing your python test program to something that does a single operation that causes an assertion to fire? Trying to figure out what you think the behavior ought to be from all this output is going to be hard.
So it seems I can't do implicit dynamic casts (passing a base-class pointer to a function taking a subclass pointer) and explicit dynamic casts at the same time.
-- Dave Abrahams Boost Consulting www.boost-consulting.com
David Abrahams wrote:
^^^ That's not very useful; it produces a syntax error. Please post something that compiles.
This will take a while, since I'm not familiar with building shared libraries, except by letting autoconf/automake do it.
Everything except wrapper.cpp is built as one shared library.
And how is wrapper.cpp built? How about just posting a Jamfile?
I can do that, but again I'd have to create a new Jamfile.
How about reducing your python test program to something that does a single operation that causes an assertion to fire? Trying to figure out what you think the behavior ought to be from all this output is going to be hard.
There are three things that go wrong: 1. calling a function that takes a pointer/reference to the SubClass with a BaseClass& that refers to a SubClass 2. dynamically casting from BaseClass 3. dynamically casting from SubClass The behavior ought to be: Testing SubClass subcl can be passed to test_subclass directly subcl can be passed to test_subclass when returned as a base class subcl can be dynamically cast from subclass subcl can be dynamically cast from base class Although I've been able to get the test cases to do that, I've been unable to get the real code to do that. Aaron -- Aaron Bentley Director of Technology Panometrics, Inc.
David Abrahams wrote:
I can tell you that in many compilers, RTTI information typically gets generated where a class' first non-inline function is defined.
Ah! That's very helpful. I'd assumed there was something special about constructors, but it was just a question of whether the class had *any* non-inline functions. I was at least able to get dynamic casting working, so I can do some ugly wrapping and get on with this project. Thanks! Aaron -- Aaron Bentley Director of Technology Panometrics, Inc.
participants (3)
-
Aaron Bentley -
Dan Haffey -
David Abrahams