Hossein
Hossein

Reputation: 26014

How to expose a pybind11::object in C?

Currently I have this signature on my C++ class :

typedef void(*CallbackFn)(bool, std::string, py::array_t<uint8_t>&);
void AddCallback(CallbackFn callback);

and in my client code I simply have :

void default_callback(bool status, std::string id, py::array_t<uint8_t>& img)
{
    auto rows = img.shape(0);
    auto cols = img.shape(1);
    auto type = CV_8UC3;

    cv::Mat img1(rows, cols, type, img.mutable_data());

    cv::imshow("from callback", img1);
    auto timenow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    std::cout << "\narg1: " << status << " arg2: " << id << " arg3: " << typeid(img).name() << " " << ctime(&timenow) << std::endl;
}

and this works just fine for C++. but now I have decided to make a DLL out of this and thus need to expose this class functionalities in C. This is where I got stuck!
I have tried using :

typedef void(*CCallbackFn)(bool, char*, unsigned char);
typedef void(*CCallbackFn)(bool, char*, void*);

but they doesn't work as I get an exception on Python part:
when using unsigned char :

TypeError: (): incompatible function arguments. The following argument types are supported:
    1. (arg0: bool, arg1: str, arg2: int) -> None

Invoked with: True, '5', array([[[193, 218, 237],
        [193, 218, 237],
        [192, 217, 235],
        ...,

       [[193, 218, 237],
        [193, 218, 237],
        [192, 217, 235],
        ...,
        [ 68,  51,  88],
        [ 69,  54,  88],
        [ 72,  58,  92]]], dtype=uint8)

and when using the void* I get :

TypeError: (): incompatible function arguments. The following argument types are supported:
    1. (arg0: bool, arg1: str, arg2: capsule) -> None

Invoked with: True, '5', array([[[195, 216, 239],
        [195, 216, 239],
        [193, 214, 236],
        ...,

       [[131, 147, 153],
        [124, 140, 146],
        [116, 126, 136],
        ...,
        [100, 108, 144],
        [104, 112, 148],
        [104, 112, 148]]], dtype=uint8)

I assumed since I'm dealing with OpenCV image, and that's an object (more over it should be passed as PyObject to me right?), void* could be a viable option, and on the C++ part I can recast that into a PyObject to get the underlying pointer to the buffer and then use it .
Since its a void *, I wouldn't also have any issues on using it as a C function signature, so everything should be fine!
but I guess this assumption doesn't hold here or I'm missing something.
What am I missing here and what are my options in exposing pybin11::objects such as pybind11::array_t in C?

Upvotes: 1

Views: 191

Answers (1)

Hossein
Hossein

Reputation: 26014

This seems like a Pybind11 bug and I ended up using a delegate callback. That is use a middle callback for converting between the two format. It is explained in detail here.

Upvotes: 1

Related Questions