I have two types of function pointers defined in my C++ that look like this:
typedef void(*CallbackFn)(bool, std::string, py::array_t<uint8_t>&);
typedef std::function<void(std::string)> LogFunction;
Class Core{
...
void myfunc1(LogFunction lg1, CallbackFn callback, int x, std::string y);
};
and I want to be able to expose them in C but I can't seem to find a way to do so. My first try was to cast these as void*
and then recast them back to their actual type. but this seems like a bad idea. So I'm clueless as how to go about this conversion.
Also the solution that I need to come-up with should be doable using C++11 at the very least.
Update:
Thank you very much for your answers. However I need to add a bit more explanation as what I'm after. I know about extern "C"
and in fact the C++
functions are exposed using this already in my DLL
. However, the problem I had was to pass the function pointers back and forth between the C and C++.
One way was to define function pointers in a way that can be directly usable by C. That is I needed to change for example :
typedef void(*CallbackFn)(bool, std::string, py::array_t<uint8_t>&);
typedef std::function<void(std::string)> LogFunction;
to its C compatible one :
typedef void(*CCallbackFn)(bool, char*, int, unsigned char, int length);
typedef void(*CLogFunction)(char* string, int length);
and use these instead. However, the disadvantage of doing this is that, the DLL is also used by C++ clients and this would be a hindrance to change everything C++ to be compatible by C, I'd lose the advantages of C++ by doing this.
Instead I though of coming up with a second way. The C++ stays the same, but for C linkage and interacting with other languages through C API, I do the conversion myself.
That is they use C style and then I convert this back to C++ in the implementation part. In order to further simplify this so I designed some defaults on C++ part as well. Meaning, suppose for the lack of a better example, the instance needs a callback function to log whatever happens. I define a callback function in case it was not given by the user and create two functions for C API specifically something roughly similar to this:
//in core.cpp for example
include "Core.h"
...
extern "C"
{
Core * core;
...
Core_API void* get_default_log_callback()
{
return (void*) core->SomeDefaultCallback();
}
Core_API void* set_log_callback(void* fn)
{
// convert/cast that to the c++ callback type
// CallbackFn,
core->SetCallback(fn_converted);
}
and the client could for example use the get_default_log_callback and use its return to set_log_call_back
.
Basically the idea here is to be able to use the C++ already defined assets.
I was stuck at this conversion process, how to convert such callback pointers to a C compatible type ( like what I showed, it'd be really easy to just cast the pointer to void* for example and write a C wrapper that accepts void* and then recast it to the proper type.
I'd like to know about this scenario as well and whether this is a good practice or the otherwise a bad one.
Question two:
Also I'd like to know if it is possible to have a conversion from for example the CCallbackFn
and CallbackFn
?
Suppose I've got a function(my C function above e.g.) in a CCalbackFn
form ,but I want to ultimately have it in CallbackFn
form(change it and call the underlying C++ that accepts CallbackFn
) ? is this possible ?
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 :
And now we want to expose this to C. and this is our C callback signature :
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:
And you update your C callback list like this, using two exposed functions, Add and Remove to add and remove any callbacks respectively :
and back in our C++ class,
AddCallback_C
methods are defined like: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:
You can get fancy with this and incorporate threads, etc as you wish.