如何在C中公开C ++函数指针? [英] How can I expose C++ function pointers in C?
问题描述
我在C ++中定义了两种类型的函数指针,如下所示:
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);
};
我想能够在C语言中公开它们,但我似乎找不到办法这样做。我的第一次尝试是将它们转换为 void *
,然后将其重铸为实际类型。但这似乎是个坏主意。所以我对如何进行转换一无所知。
另外,我至少需要使用C ++ 11来解决我需要的解决方案。
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.
非常感谢您的回答。但是,我需要补充一些解释。我知道外部 C
,实际上 C ++
函数已经在我的 DLL
。但是,我遇到的问题是在C和C ++之间来回传递函数指针。
一种方法是以可以直接使用的方式定义函数指针例如,我需要更改:
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;
到与C兼容的一个:
typedef void(*CCallbackFn)(bool, char*, int, unsigned char, int length);
typedef void(*CLogFunction)(char* string, int length);
并使用它们代替。但是,这样做的缺点是,DLL也被C ++客户端使用,这将成为将C ++的所有内容都更改为与C兼容的障碍,这样做会使我失去C ++的优势。
相反,我虽然想出了第二种方法。 C ++保持不变,但是对于C链接和通过C API与其他语言的交互,我自己进行转换。
那是他们使用C样式,然后在实现部分将其转换回C ++ 。为了进一步简化此操作,因此我还为C ++部分设计了一些默认值。意思是,假设缺少一个更好的例子,该实例需要一个回调函数来记录发生的任何事情。我定义了一个回调函数,以防用户未提供该函数,并为C API创建两个函数,具体类似于以下内容:
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);
}
例如,客户端可以使用get_default_log_callback并使用其返回 set_log_call_back
。
基本上,这里的想法是能够使用C ++已经定义的资产。
我被困在这个转换过程中,如何将这些回调指针转换为C兼容类型(就像我展示的那样,例如,将指针强制转换为void *并编写C包装器真的很容易接受void *,然后将其重铸为适当的类型。
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.
我也想知道是否有可能例如 CCallbackFn
和 CallbackFn
的转换?
假设我有一个函数(我上面的C函数例如)以 CCalbackFn
的形式出现,但是我最终希望以 CallbackFn
的形式出现(将其更改)并调用接受 CallbackFn
)的基础C ++?这可能吗?
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 ?
推荐答案
<我最终提出了自己的解决方案,我自己称之为委派回调方法H!这里的想法是,创建一个转移而不是直接使用C回调,而是创建一个中间回调,该中间回调充当两个API之间的转换器。
例如,假设我的C ++类具有仅接受具有此签名的回调的方法:
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<uint8_t>&);
现在我们要将其公开给C,这是我们的C回调签名:
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);
现在我们如何从第一个转到第二个,反之亦然?我们在 CallbackFn
类型的C ++类中创建一个新的回调,并在其中执行C回调。因此,使用间接调用,我们可以轻松地将C和C ++ API之间的签名解耦,并使用最适合每个API的签名。
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<uint8_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));
}
}
您将更新C回调列表这样,使用两个公开的函数Add和Remove分别添加和删除所有回调:
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);
}
}
并返回我们的C ++类, AddCallback_C
方法的定义如下:
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);
}
只需将回调添加/删除到回调列表中即可。就这样。
现在,当我们实例化C ++代码时,我们需要将 DelegateCCallback
添加到回调列表中,因此,当所有C ++回调都被执行时,它也会同时执行,它将遍历所有C回调并逐一执行它们。
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.
例如,在我的情况下,回调需要在Python模块中运行,因此在我的构造函数中,我必须执行以下操作:
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<uint8_t>& img)
{
this->DelegateCCallback(status, id, img);
}));
//...
}
您可以获得看中这一点,并根据需要合并线程等。
You can get fancy with this and incorporate threads, etc as you wish.
这篇关于如何在C中公开C ++函数指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!