How can I expose C++ function pointers in C?

前端 未结 4 1458
盖世英雄少女心
盖世英雄少女心 2021-01-19 16:57

I have two types of function pointers defined in my C++ that look like this:

typedef void(*CallbackFn)(bool, std::str         


        
4条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-19 17:36

    I ultimately came up with my own solution which I myself refer to as "Delegating Callbacks" approach! The idea here is that, instead of directly use the C callback, you create a diversion, you create an intermediate callback that acts as a translator between the two APIs. For example, suppose my C++ class has a method that accepts only callbacks with this signature :

    typedef void(*CallbackFn)(bool, std::string, py::array_t&);
    

    And now we want to expose this to C. and this is our C callback signature :

    typedef void(*CCallbackFn)(bool, const char*, unsigned char*, int rows, int cols);
    

    Now how do we go from the first to the second one or vice versa? We create a new callback in our C++ class of type CallbackFn, and inside it execute the C callbacks. So using an indirect call, we can easily decouple the signatures between the C and C++ APIs and use the ones that are most suitable for each.

    To make it more concrete we need to have something like this:

    CORE_API void Core::DelegateCCallback(bool status, std::string id, py::array_t& img)
    {
        //here is used a std::map to store my c-callbacks you can use
        //vector or anything else that you like
        for (auto item: this->callbackMap_c)
        {
            //item.first is our callback, so use it like a function 
            item.first(status, id.c_str(), img.mutable_data(), img.shape(0), img.shape(1));
        }
    }
    
    

    And you update your C callback list like this, using two exposed functions, Add and Remove to add and remove any callbacks respectively :

    extern "C"
    {
    //Core is our C++ class for example
    Core* core = nullptr;
    ...
        CORE_API void AddCallback(CCallbackFn callback)
        {
            core->AddCallback_C(callback);
        }
    
        CORE_API void RemoveCallback(CCallbackFn callback)
        {
            core->RemoveCallback_C(callback);
        }
    }
    

    and back in our C++ class, AddCallback_C methods are defined like:

    CORE_API void Core::AddCallback_C(CCallbackFn callback)
    {
        auto x = this->callbackMap_c.emplace(callback, typeid(callback).name());
    }
    
    CORE_API void Core::RemoveCallback_C(CCallbackFn callback)
    {
        this->callbackMap_c.erase(callback);
    }
    

    Just adding/removing the callback to the callback list. That's all. Now when we instantiate our C++ Code, we need to add the DelegateCCallback to the callback list, so when all C++ callbacks are executed this one executes too and with it, it will loop through all the C callbacks and executes them one by one.

    For example in my case, the callbacks needed to be run in a Python module, so in my constructor I had to do something like this:

    
    CORE_API Core::Core(LogFunction logInfo)
    {
        //....
        // add our 'Callback delegate' to the list of callbacks
        // that would run.  
        callbackPyList.attr("append")(py::cpp_function([this](bool status, std::string id, py::array_t& img)
                                                             {
                                                                this->DelegateCCallback(status, id, img);
                                                             }));
    
    //...
    }
    

    You can get fancy with this and incorporate threads, etc as you wish.

提交回复
热议问题