[C++-sig] void* (void pointer) function arguments

Holger Joukl Holger.Joukl at LBBW.de
Mon Aug 15 17:11:07 CEST 2011


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 at 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



More information about the Cplusplus-sig mailing list