Commentary
There are several unrelated issues with your example code:
object b = a.attr("A"); -- This gets the class type. You need to run the constructor to create an instance of A. That means a.attr("A")();.
- In the Python script,
cppcallback is a local variable in __init__ and callback functions. In start_consume it's undefined. This should be a member variable, as in self.cppcallback.
- Having pointers to
object as parameters to your callback handler is debatable. I think it's fine to pass them by value.
Solution
Python Script
I wrote a simplified script that mimics what you've got:
class A:
def __init__(self):
self.handler = None
def callback(self, handler):
self.handler = handler
self.do_something()
def do_something(self):
self.handler(1,2,3,"foo")
Using Plain Functions
This approach is quite straightforward.
First create a callable object using make_function.
Then import the Python test_module from our script, construct and instance of A and call it's callback member passing it our callable object as parameter.
#include <boost/python.hpp>
namespace bp = boost::python;
void callback_handler(bp::object ch
, bp::object method
, bp::object properties
, std::string const& body)
{
std::cout << "in handler: " << body << std::endl;
}
int main()
{
Py_Initialize();
try {
bp::object h = bp::make_function(callback_handler);
bp::object a = bp::import("test_module");
bp::object b = a.attr("A")(); // Construct instance of A
b.attr("callback")(h);
} catch (bp::error_already_set const &) {
PyErr_Print();
}
return 0;
}
Console output:
>example_1.exe
in handler: foo
Using boost::function
Using boost::function object for the callback handler is a little trickier, since boost::function is not supported by boost::python by default. Thus, we first need to enable support for boost::function, as described in this answer by Tanner Sansbury.
NB: This snippet needs to come before including boost/python.hpp!
// ============================================================================
// Enable support for boost::function
// See https://stackoverflow.com/a/18648366/3962537
// ----------------------------------------------------------------------------
#include <boost/function.hpp>
#include <boost/function_types/components.hpp>
// ----------------------------------------------------------------------------
namespace boost { namespace python { namespace detail {
// ----------------------------------------------------------------------------
// get_signature overloads must be declared before including
// boost/python.hpp. The declaration must be visible at the
// point of definition of various Boost.Python templates during
// the first phase of two phase lookup. Boost.Python invokes the
// get_signature function via qualified-id, thus ADL is disabled.
// ----------------------------------------------------------------------------
/// @brief Get the signature of a boost::function.
template <typename Signature>
inline typename boost::function_types::components<Signature>::type
get_signature(boost::function<Signature>&, void* = 0)
{
return typename boost::function_types::components<Signature>::type();
}
// ----------------------------------------------------------------------------
}}} // namespace boost::python::detail
// ============================================================================
The rest is very similar to the first scenario.
#include <iostream>
#include <boost/python.hpp>
namespace bp = boost::python;
void callback_handler(bp::object ch
, bp::object method
, bp::object properties
, std::string const& body
, std::string const& extra)
{
std::cout << "in handler: " << body << extra << std::endl;
}
int main()
{
Py_Initialize();
try {
typedef boost::function<void(bp::object, bp::object, bp::object, std::string)> handler_fn;
handler_fn my_handler(boost::bind(callback_handler, _1, _2, _3, _4, " bar"));
bp::object h = bp::make_function(my_handler);
bp::object a = bp::import("test_module");
bp::object b = a.attr("A")();
b.attr("callback")(h);
} catch (bp::error_already_set const &) {
PyErr_Print();
}
return 0;
}
Console output:
>example_2.exe
in handler: foo bar