如何在C中公开C ++函数指针? [英] How can I expose C++ function pointers in C?

查看:85
本文介绍了如何在C中公开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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆