I am wrapping a library which was written in C++ to Python API
libwebqq

There is a type which is defined in boost function .

typedef boost::function<void (std::string)> EventListener;

Python level can define "EventListener" variable callbacks.

There is also a map structure in C++ level which is event_map in class Adapter.
The key type of event_map is a QQEvent enum type and the value type of event_map is class "Action" which wraps EvenListener.

"register_event_handler" in class Adapter is the API to register a callback function to related event.
And C++ back end will call it if event happened. But we need to implement the callbacks in python level. And I wrapped the callback type in "callback.i"

The problem is , when I call the register_event in test python script, a type error always occurs:

1 Answer
1

The problem seems to be that you haven't included any code to map from a Python callable to your EventListener class. It's not provided for free, although it's something that comes up fairly regularly, e.g. here which acted as a reference for part of this answer.

Your question has quite a lot of code that's not really relevant to the problem and not quite complete either so I've produced a minimal header file to demonstrate the problem and solution with:

Hopefully we're on the same page now. There's a single version of register_handler that expects an object of type EventListener (SWIG's generated proxy for the type to be precise). We're not trying to pass an EventListener in when we call that function though - it's a Python Callable instead, with not much known on the C++ side - certainly not a type match or implicitly convertible. So we need to add some glue in our interface to mush the Python type into the real C++ type.

We do that by defining an entirely new type, which only exists internally to the SWIG wrapper code (i.e. within %{ }%). The type PyCallback has one purpose: hold a reference to the real Python thing we're working with and make it look/feel like a function in C++.

Once we've added that PyCallback implementation detail (which nobody gets to see) we then need to add another overload for register_handler, which takes a PyObject* directly and constructs the PyCallback+EventListener for us. Since this only exists for the purpose of wrapping we use %inline to declare, define and wrap all within the SWIG interface file. So our interface file now looks like:

At this point we now have enough for the original test Python to run successfully.

It's worth noting that we could have chosen to hide the original overload of register_handler, but in this instance I prefer not to - it's more of a feature than a mistake to leave that visible because it permits you to manipulate C++ defined callbacks also, e.g. you could get/set them from the Python side and expose some core ones as global variables.

Fist of all , thanks for your kindly support. I added the Pycallback class and "extend Action -> setCallback" in file "libwebqq.i", and also changed "inline register_event_handler" to " extend Adapter" because the "register_event_handler" is a method of Adapter. The project can build successfully. But when the C++ level call the python defined callabcks , a segment fault will happen. The error message is "swig/python detected a memory leak of type 'EventListener *', no destructor found."
–
user1530527Jul 18 '12 at 3:13

@user1530527 - that sounds like several problems. Firstly you need to make sure that when SWIG wraps a type it has a definition of the class sufficient to be able to legally call delete. Secondly you might have locking issues if your code is multi-threaded.
–
Flexo♦Jul 19 '12 at 8:24