How to set python function as callback for c++ using pybind11?


typedef bool (*ftype_callback)(ClientInterface* client, const Member* member ,int member_num);

struct Member{
    char x[64];
    int y;

class ClientInterface {
    virtual int calc()=0;
    virtual bool join()=0;
    virtual bool set_callback(ftype_callback on_member_join)=0;

It is from SDK which I can call the client from dynamic library in c++ codes.

bool cb(ClientInterface* client, const Member* member ,int member_num) {
    // do something

I want to port it to python bindings use pybind11. How do I set_callback in python?

I have seen the doc and try:

PYBIND11_MODULE(xxx, m) {
    m.def("set_callback", [](xxx &self, py::function cb ){

The code just failed to compile.

My question, how do I convert the py::function to ftype_callback or there is other way to make it?

Asked By: HALF9000



You need a little C++ to get things going. I’m going to use a simpler structure to make the answer more readable. In your binding code:

#include <pybind11/pybind11.h>

#include <functional>
#include <string>

namespace py = pybind11;

struct Foo
    int i;
    float f;
    std::string s;

struct Bar
    std::function<bool(const Foo &foo)> python_handler;
    std::function<bool(const Foo *foo)> cxx_handler;

        cxx_handler = [this](const Foo *foo) { return python_handler(*foo); };

PYBIND11_MODULE(example, m)
    py::class_<Foo>(m, "Foo")  //
        .def_readwrite("i", &Foo::i)
        .def_readwrite("f", &Foo::f)
        .def_readwrite("s", &Foo::i);

    py::class_<Bar>(m, "Bar")  //
        .def_readwrite("handler", &Bar::python_handler);

Here, Foo is the object that is passed to the callback, and Bar is the object that needs its callback function set. Since you use pointers, I have wrapped the python_handler function with cxx_handler that is meant to be used in C++, and converted the pointer to reference.

To be complete, I’ll give a possible example of usage of the module here:

import module.example as impl

class Bar:
    def __init__(self): = impl.Bar() = self.handler
    def handler(self, foo):
        return True

I have used this structure successfully in one of my projects. I don’t know how you want to proceed, but perhaps if you don’t want to change your original structure you can write wrapper classes upon them that use the given structure.


I thought that you controlled the structure when I wrote the answer above (I’ll keep it for anyone who needs it). If you have a single cli instance, you can do something like:

using Handler = std::function<bool(std::string, int, int)>;

Handler handler;

bool cb(ClientInterface *client, const Member *member, int member_num)
    return handler(std::string(member->x), member->y, member_num);

// We have created cli somehow

// cli->set_callback(cb);

// cli->join();

PYBIND11_MODULE(example, m)
    m.def("set_callback", [](Handler h) { handler = h; });

If you have multiple ClientInterface instances, you can map ClientInterface pointers to handlers and call the appropriate handler in the cb function based on given ClientInterface pointer.

Note: I haven’t tested the above with a python script but it should work.

Another Update

If you want to handle multiple instances, the code can roughly look like this:

using Handler = std::function<bool(std::string, int, int)>;

std::map<ClientInterface *, handler> map;

bool cb(ClientInterface *client, const Member *member, int member_num)
    // Check if <client> instance exists in map
    return map[client](std::string(member->x), member->y, member_num);

PYBIND11_MODULE(example, m)
    m.def("set_callback", [](int clientid, Handler h) 
        // Somehow map <clientid> to <client> pointer
        map[client] = h;

Note that this isn’t a runnable code and you need to complete it.

Answered By: Shahriar

you can get data using python types in pybind11

make sure you have #include <pybind11/functional.h>

// c++
using PyCallback = std::function<void(pybind11::bytearray)>;

class Haha
    void setCallback(PyCallback& pyfn) {
        m_pyfn = pyfn;
    void onDataAvaiable(char* buf, int len) {
        m_pyfn(pybind11::bytearray(buf, len));
    PyCallback m_pyfn;

PYBIND11_MODULE(haha, m) {
    pybind11::class_<Haha>(m, "Haha")
        .def("setCallback", &Haha::setCallback);

// python
def fn(data):

hahaInstance = m.Haha()
hahaInstance .setCallback(fn)
while True:
    // block to make sure hahaInstance is always running, then callback will print data
Answered By: yan li
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.