Hi, I'm trying to wrap a C++-API that uses void* to pass around arbitrary, application-specific stuff.
I am a bit unsure about how to work the void-pointers. A viable way seems to wrap the original API functions with thin wrappers that take a boost::python::object and hand the "raw" PyObject* into the original function: // Expose the method as taking any bp::object and hand the "raw" PyObject pointer to // the void-ptr-expecting API-function int Worker_destroy2_DestructionCallbackPtr_constVoidPtr( Worker* worker, DestructionCallback* cb, bp::object& closure) { return worker->destroy2(cb, closure.ptr()); } The information available is then retrieved by some callback function (which is pure virtual in the API and needs to be overridden in Python). Before calling into Python I then cast the void* back to a PyObject*, make a boost::python::object from it and invoke the Python override: class DestructionCallbackWrap : public DestructionCallback, public bp::wrapper<DestructionCallback> { virtual void callback(Worker* worker, void* closure) { std::cout << ">>> " << __PRETTY_FUNCTION__ << std::endl; // everything ending up here from Python side is a PyObject bp::handle<> handle(bp::borrowed(reinterpret_cast<PyObject*> (closure))); bp::object closureObj(handle); this->get_override("callback")(bp::ptr(worker), closureObj); std::cout << "<<< " << __PRETTY_FUNCTION__ << std::endl; } }; As I've tried to gather information about void* handling with boost.python back and forth without finding much (an FAQ entry seems to have existed once upon a time?) - is this a sane approach? Or is there some automagical void* or const void* handling in boost.python that I am totally missing? Any hint much appreciated, Holger P.S.: Here's a full minimal example for reference: // file void_ptr_cb.hpp // API to wrap class Worker; class DestructionCallback { public: DestructionCallback() {} virtual ~DestructionCallback() {} virtual void callback(Worker* worker, void* closure) = 0; }; class Worker { public: Worker() {} virtual ~Worker() {} virtual int destroy() { return 0; } int destroy2(DestructionCallback* cb, const void* closure=NULL) { std::cout << ">>> " << __PRETTY_FUNCTION__ << std::endl; cb->callback(this, const_cast<void*>(closure)); std::cout << "<<< " << __PRETTY_FUNCTION__ << std::endl; return 0; } private: Worker(const Worker& worker); }; // file wrap_void_ptr_cb.cpp #include <boost/python.hpp> #include <iostream> #include "void_ptr_cb.hpp" namespace bp = boost::python; // Helper classes needed for boost.python wrapping class DestructionCallbackWrap : public DestructionCallback, public bp::wrapper<DestructionCallback> { virtual void callback(Worker* worker, void* closure) { std::cout << ">>> " << __PRETTY_FUNCTION__ << std::endl; // everything ending up here from Python side is a PyObject bp::handle<> handle(bp::borrowed(reinterpret_cast<PyObject*> (closure))); bp::object closureObj(handle); this->get_override("callback")(bp::ptr(worker), closureObj); std::cout << "<<< " << __PRETTY_FUNCTION__ << std::endl; } }; class WorkerWrap : public Worker, public bp::wrapper<Worker> { public: virtual int destroy() { if (bp::override f = this->get_override("destroy")) return f(); // *note* return Worker::destroy(); } int default_destroy() { return this->Worker::destroy(); } }; // Expose the method as taking any bp::object and hand the "raw" PyObject pointer to // the void-ptr-expecting API-function int Worker_destroy2_DestructionCallbackPtr_constVoidPtr( Worker* worker, DestructionCallback* cb, bp::object& closure) { return worker->destroy2(cb, closure.ptr()); } BOOST_PYTHON_MODULE(void_ptr_cb) { bp::class_<DestructionCallbackWrap, boost::noncopyable> ("DestructionCallback", bp::init<>()) .def("callback", bp::pure_virtual(&DestructionCallback::callback)) ; bp::class_<WorkerWrap, boost::noncopyable>("Worker") .def("destroy", &Worker::destroy, &WorkerWrap::default_destroy) .def("destroy2", &Worker_destroy2_DestructionCallbackPtr_constVoidPtr, (bp::arg("cb"), bp::arg("closure")=bp::object())) ; }; # file Jamroot # Run with: # BOOST_ROOT=/var/tmp/lb54320/boost_apps/boost_1_46_1 BOOST_BUILD_PATH=/var/tmp/lb54320/boost_apps/boost_1_46_1 \ # /var/tmp/$USER/boost_apps/boost_1_46_1/bjam -d+2 toolset=gcc-4.5.1 \ # --build-dir=/var/tmp/$USER/boost_apps/boost_1_46_1/build/py2.7/boost/1.46.1/ cxxflags="-DDEBUG_HIGH \ # -DBOOST_PYTHON_TRACE_REGISTRY" link=shared threading=multi variant=release void_ptr_cb # # get the environment variable "USER" import os ; local _USER = [ os.environ USER ] ; #ECHO $(_USER) ; local _WORKDIR = /var/tmp/$(_USER)/boost_apps ; local _BOOST_MODULE = boost_1_46_1 ; local _BOOST_ROOT = $(_WORKDIR)/$(_BOOST_MODULE) ; local _BOOST_VERSION = 1.46.1 ; #ECHO $(_BOOST_ROOT) ; use-project boost : $(_BOOST_ROOT) ; # Set up the project-wide requirements that everything uses the # boost_python library from the project whose global ID is # /boost/python. project minimal_void_ptr_cb : requirements <library>/boost/python//boost_python <dll-path><variant>debug:$(_BOOST_ROOT)/stage/py2.7/boost/$ (_BOOST_VERSION)/debug/lib <dll-path><variant>release:$(_BOOST_ROOT)/stage/py2.7/boost/$ (_BOOST_VERSION)/lib ; python-extension void_ptr_cb : # sources + // Add all files here otherwise we get undefined symbol errors like wrap_void_ptr_cb.cpp : # requirements * : # default-build * : # usage-requirements * ; #!/apps/local/gcc/4.5.1/bin/python2.7 # file test.py import os import sys _USER = os.getenv("USER") EXPATH = ('/var/tmp/%s/boost_apps/boost_1_46_1/build/py2.7/boost/1.46.1/' 'minimal_void_ptr_cb/gcc-4.5.1/release/threading-multi' % (_USER)) sys.path.insert(1, EXPATH) import void_ptr_cb class MyDestructionCallback(void_ptr_cb.DestructionCallback): def callback(self, worker, closure): print "MyDestructionCallback.callback(%s, %s, %s)" % (self, worker, closure) class MyDestructionCallback2(void_ptr_cb.DestructionCallback): def callback(self, worker, closure): print "MyDestructionCallback.callback2(%s, %s, %s)" % (self, worker, closure) closure.do('what?') class SomeClass(object): def do(self, something=None): print "SomeClass.do(something=%s)" % repr(something) md = MyDestructionCallback() md.callback('worker', 'closure') print w = void_ptr_cb.Worker() w.destroy() w.destroy2(md, None) print some = SomeClass() w = void_ptr_cb.Worker() w.destroy() w.destroy2(md, some) some.do('else') print md2 = MyDestructionCallback2() w = void_ptr_cb.Worker() w.destroy() w.destroy2(md2, SomeClass()) Test run output: 0 holger@devel .../minimal_void_ptr_cb $ ./test.py MyDestructionCallback.callback(<__main__.MyDestructionCallback object at 0x2a6cc0>, worker, closure) >>> int Worker::destroy2(DestructionCallback*, const void*) >>> virtual void DestructionCallbackWrap::callback(Worker*, void*) MyDestructionCallback.callback(<__main__.MyDestructionCallback object at 0x2a6cc0>, <void_ptr_cb.Worker object at 0x2a6cf0>, None) <<< virtual void DestructionCallbackWrap::callback(Worker*, void*) <<< int Worker::destroy2(DestructionCallback*, const void*) >>> int Worker::destroy2(DestructionCallback*, const void*) >>> virtual void DestructionCallbackWrap::callback(Worker*, void*) MyDestructionCallback.callback(<__main__.MyDestructionCallback object at 0x2a6cc0>, <void_ptr_cb.Worker object at 0x2a6d20>, <__main__.SomeClass object at 0x2b7710>) <<< virtual void DestructionCallbackWrap::callback(Worker*, void*) <<< int Worker::destroy2(DestructionCallback*, const void*) SomeClass.do(something='else') >>> int Worker::destroy2(DestructionCallback*, const void*) >>> virtual void DestructionCallbackWrap::callback(Worker*, void*) MyDestructionCallback.callback2(<__main__.MyDestructionCallback2 object at 0x2a6cf0>, <void_ptr_cb.Worker object at 0x2a6d50>, <__main__.SomeClass object at 0x2b7730>) SomeClass.do(something='what?') <<< virtual void DestructionCallbackWrap::callback(Worker*, void*) <<< int Worker::destroy2(DestructionCallback*, const void*) Landesbank Baden-Wuerttemberg Anstalt des oeffentlichen Rechts Hauptsitze: Stuttgart, Karlsruhe, Mannheim, Mainz HRA 12704 Amtsgericht Stuttgart _______________________________________________ Cplusplus-sig mailing list Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig