Boost.Python and slightly more tricky inheritance

Greetings. 1. First, about a bug (Boost 1.33.0, MSVC 7.1): After exporting void test1( const char * ) { } void test2( std::string ) { } we get an assertion failure _BLOCK_TYPE_IS_VALID( ... ) on std::strings longer than a dozen of characters: test1( 'hello' ) # OK test2( 'hello' ) # OK test1( 'how do you do, tell me please' ) # OK test2( 'how do you do, tell me please' ) # failure 2a. From `Exposing Classes' tutorial section: "If Base is polymorphic, Derived objects which have been passed to Python via a pointer or reference to Base can be passed where a pointer or reference to Derived is expected." Now, that's a great feature, but I couldn't help noticing that what Boost.Python actually does, is even more useful: not only such pointers can be passed instead of Derived ones, but their Python counterparts _are_ fully qualified Derived representations, with all the methods and everything. Probably worth mentioning in dox that way. 2b. Now, suppose we have one more derived class: class Base { ... }; class Derived: public Base { ... }; class HiddenImplementation: public Derived { ... }; with Derived being no more than an interface. Naturally, we don't want to export HiddenImplementation to Python, but still want to retain automatic conversions from Base* to Derived* whenever pointer to HiddenImplementation object is passed to Python. Any way to do this? Without hack-ins? I was able to trick the system by defining the fake empty implemetation class class HiddenImplementation: public Derived { }; right before the BOOST_PYTHON_MODULE and exporting it out there, but this kind of `solution' really annoys me. And I don't understand why it works, either :) . Oh, and it's only a partial solution, too: we do not have to access the real implementation class, but we still have to export something. Comments? -- TIA, Vladimir mailto:ardatur@mail.ru

Vladimir Pozdyayev <ardatur@mail.ru> writes:
Greetings.
Hi! The C++-sig is usually a better place for Boost.Python-related posts (http://www.boost.org/more/mailing_lists.htm#cplussig).
1. First, about a bug (Boost 1.33.0, MSVC 7.1):
After exporting void test1( const char * ) { } void test2( std::string ) { } we get an assertion failure _BLOCK_TYPE_IS_VALID( ... ) on std::strings longer than a dozen of characters: test1( 'hello' ) # OK test2( 'hello' ) # OK test1( 'how do you do, tell me please' ) # OK test2( 'how do you do, tell me please' ) # failure
And you think this is a bug in Boost code? Can you post a reproducible test case? I bet you didn't use Boost.Build to create your extension module, as recommended in the Boost.Python documentation. You probably are linking with multiple copies of the runtime.
2a. From `Exposing Classes' tutorial section:
"If Base is polymorphic, Derived objects which have been passed to Python via a pointer or reference to Base can be passed where a pointer or reference to Derived is expected."
Now, that's a great feature, but I couldn't help noticing that what Boost.Python actually does, is even more useful: not only such pointers can be passed instead of Derived ones, but their Python counterparts _are_ fully qualified Derived representations, with all the methods and everything. Probably worth mentioning in dox that way.
I think I would say something slightly different, but it's a good point. Joel? In fact, it will work as long as the actual dynamic type of the object has been exposed to Python. However, if the type is something derived from Derived, Boost.Python may not be able to determine the most-derived type that's been exposed.
2b. Now, suppose we have one more derived class:
class Base { ... }; class Derived: public Base { ... }; class HiddenImplementation: public Derived { ... };
with Derived being no more than an interface. Naturally, we don't want to export HiddenImplementation to Python, but still want to retain automatic conversions from Base* to Derived* whenever pointer to HiddenImplementation object is passed to Python.
Any way to do this? Without hack-ins?
Not without a hack, no. But the hack might be simple: #include <boost/python/object/inheritance.hpp> // non-public implementation details ... boost::python::object::register_conversion<Derived,HiddenImplementation>(); boost::python::object::register_conversion<HiddenImplementation,Derived>();
I was able to trick the system by defining the fake empty implemetation class class HiddenImplementation: public Derived { }; right before the BOOST_PYTHON_MODULE and exporting it out there, but this kind of `solution' really annoys me. And I don't understand why it works, either :) . Oh, and it's only a partial solution, too: we do not have to access the real implementation class, but we still have to export something.
I don't think there's any chance of a solution that doesn't require access to the real implementation class. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Hello David, DA> The C++-sig is usually a better place for Boost.Python-related posts DA> (http://www.boost.org/more/mailing_lists.htm#cplussig). Thought that, but had to stick to whatever mailing list I am already subscribed, for this time :( .
1. First, about a bug (Boost 1.33.0, MSVC 7.1): ...<C++ code>... test1( 'hello' ) # OK test2( 'hello' ) # OK test1( 'how do you do, tell me please' ) # OK test2( 'how do you do, tell me please' ) # failure DA> And you think this is a bug in Boost code? Can you post a DA> reproducible test case?
Well, that was the test case, and I hope it's reproducible... okay, here's the full source, if that's what you mean: #include <boost/python.hpp> #include <string> void test1( const char * ) { } void test2( std::string ) { } BOOST_PYTHON_MODULE( getting_started1 ) { boost::python::def( "test1", test1 ); boost::python::def( "test2", test2 ); } DA> I bet you didn't use Boost.Build to create your extension module, as DA> recommended in the Boost.Python documentation. You probably are DA> linking with multiple copies of the runtime. I followed instructions in Building and Testing / Using the IDE for your own projects section. Dox warn about compile and link errors possibility when wrong project settings are used, but say nothing about runtime failures, AFAICS. Now that you've mentioned that, I went and stuffed the above code right into \libs\python\example\getting_started1.cpp, then bjammed it. Got the same assertion failure in Python session. Does this count? DA> #include <boost/python/object/inheritance.hpp> // non-public implementation details DA> ... DA> boost::python::object::register_conversion<Derived,HiddenImplementation>(); DA> boost::python::object::register_conversion<HiddenImplementation,Derived>(); Thanks, I'll try this.
I was able to trick the system by defining the fake empty implemetation class class HiddenImplementation: public Derived { }; right before the BOOST_PYTHON_MODULE and exporting it out there, but this kind of `solution' really annoys me. And I don't understand why it works, either :) . Oh, and it's only a partial solution, too: we do not have to access the real implementation class, but we still have to export something.
DA> I don't think there's any chance of a solution that doesn't require DA> access to the real implementation class. Er... The solution above is exactly the one which does not require access to the real implementation class, because it exports the fake class instead. I was just wondering whether it could possibly be legal. And whether we could do without exporting any implementation class at all. -- Best regards, Vladimir mailto:pvv@ugnn.ru

Hello David, DA>> And you think this is a bug in Boost code? Can you post a DA>> reproducible test case? DA>> I bet you didn't use Boost.Build to create your extension module, as DA>> recommended in the Boost.Python documentation. You probably are DA>> linking with multiple copies of the runtime. VP> Now that you've mentioned that, I went and stuffed the above code VP> right into \libs\python\example\getting_started1.cpp, then bjammed it. VP> Got the same assertion failure in Python session. Does this count? On the second thought, I replaced boost_python.dll built with IDE projects with boost_python.dll built by bjam, and the assertion disappeared. Oh well. Then I put boost_python_debug.dll built with IDE into the app dir (renaming to boost_python.dll, of course), and tried again. No assertion. Putting back release version of the dll brought the failure back. So, the assertion only appears when using release version built using \libs\python\build\VisualStudio\boost_python.dsw I don't claim that it is Boost code that has an error---but surely there _is_ a bug somewhere in there? Maybe MSVC projects, maybe MSVC itself :) . -- Best regards, Vladimir mailto:pvv@ugnn.ru

Hello David, VP> So, the assertion only appears when using release version built using VP> \libs\python\build\VisualStudio\boost_python.dsw Correction: probably just release version built by anything, 'cause I didn't try bjammed release version. VP> I don't claim that it is Boost code that has an error---but surely VP> there _is_ a bug somewhere in there? Maybe MSVC projects, maybe MSVC VP> itself :) . After I built release Boost.Python version with "Multi-threaded Debug DLL" runtime, the assertion disappeared again. Hmm... I rather expected independency of app and Boost.Python in that matter. Should we call it "feature" then, instead of "bug" :) ? Pity that dox don't mention it. -- Best regards, Vladimir mailto:pvv@ugnn.ru

Hello David, VP> On the second thought, I replaced boost_python.dll built with IDE VP> ... VP> After I built release Boost.Python version with "Multi-threaded Debug VP> DLL" runtime, the assertion disappeared again. VP> ... My apologies for making so much noise :) , here's the last update for today: all of the above experiments were done on bjammed extension module. When I tried the same dll juggling on IDE-built extension module, the problem stayed there all the time, with bjam debug, IDE debug and IDE release versions of boost_python.dll. Considering that, I hope the problem is easily reproducible. -- Best regards, Vladimir mailto:pvv@ugnn.ru

Hello David,
2b. Now, suppose we have one more derived class: class Base { ... }; class Derived: public Base { ... }; class HiddenImplementation: public Derived { ... }; with Derived being no more than an interface. Naturally, we don't want to export HiddenImplementation to Python, but still want to retain automatic conversions from Base* to Derived* whenever pointer to HiddenImplementation object is passed to Python. Any way to do this? Without hack-ins?
DA> Not without a hack, no. But the hack might be simple: DA> #include <boost/python/object/inheritance.hpp> // non-public implementation details DA> ... DA> boost::python::object::register_conversion<Derived,HiddenImplementation>(); DA> boost::python::object::register_conversion<HiddenImplementation,Derived>(); Thanks again for the advice---though it didn't work as is, I was able to track down the reason, and came up with a solution. Here it is, in case you, or anyone else, is interested. First, I should mention that such conversions for function args passed from Python are always there---even if we don't mention implementation class in the extension module at all. What disappears in the latter case, is similar conversions for values being returned to Python. The solution is actually to pretend that HiddenImp objects are some sort of Derived ones: objects::copy_class_object( type_id< Derived >(), type_id< HiddenImplementation >() ); instead of calling "class_< HiddenImplementation >...". As before, we don't even need access to actual HiddenImp declaration. It is enough to have a fake class HiddenImplementation: public Derived { }; one. At least, it is enough for MSVC, and HiddenImplementation defined in a separate DLL and not mentioned anywhere else in the extension module. -- Best regards, Vladimir mailto:ardatur@mail.ru
participants (3)
-
David Abrahams
-
Vladimir Pozdyayev
-
Vladimir Pozdyayev