简体   繁体   English

我可以从C调用std :: function吗?

[英]Can I call a std::function from C?

I have some C++ code that returns a std::function . 我有一些返回std::function C ++代码。 I would like to call this from some C code. 我想从一些C代码中调用它。 Is this possible? 这可能吗? As an example I have the following code: 例如,我有以下代码:

typedef std::function<int(int)> AdderFunction;

AdderFunction makeAdder(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

extern "C" {
    AdderFunction makeCAdder(int amount) {
        return makeAdder(amount);
    }
}

with clang++ -std=c++11 test.cpp it results in the following warning: 使用clang++ -std=c++11 test.cpp会导致以下警告:

'makeCAdder' has C-linkage specified, but returns user-defined type 'AdderFunction' (aka 'function<int (int)>') which is incompatible with C

I understand why this is happening, but wondering if there is a pattern to make it possible? 我了解为什么会这样,但是想知道是否有某种模式可以实现?

The most portable method to interface between C/C++ will be to use pointers to pass data between languages and use non-member functions to make function calls. 在C / C ++之间进行接口连接的最可移植的方法是使用指针在语言之间传递数据,并使用非成员函数进行函数调用。

The .h file: .h文件:

#ifdef __cplusplus
extern "C" {
#endif

   // Declare the struct.
   struct Adder;

   // Declare functions to work with the struct.
   Adder* makeAdder(int amount);

   int invokeAdder(Adder* adder, int n);

   void deleteAdder(Adder* adder);

#ifdef __cplusplus
}
#endif

Implement them in a .cpp file as: 在.cpp文件中以以下方式实现它们:

#include <functional>

typedef std::function<int(int)> AdderFunction;
struct Adder
{
   AdderFunction f;
};

AdderFunction makeAdderFunction(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

Adder* makeAdder(int amount)
{
   Adder* adder = new Adder;
   adder->f = makeAdderFunction(amount);
   return adder;
}

int invokeAdder(Adder* adder, int n)
{
   return adder->f(n);
}

void deleteAdder(Adder* adder)
{
   delete adder;
}

It's not possible to call a std::function from C, because C doesn't support the language features that are required. 从C调用std::function是不可能的,因为C不支持所需的语言功能。 C doesn't have templates, access modifiers, callable objects, virtual methods, or anything else that std::function could use under the hood. C没有模板,访问修饰符,可调用对象,虚拟方法或std::function可能在幕后使用的任何其他内容。 You need to come up with a strategy that C can understand. 您需要提出C可以理解的策略。

One such strategy is to copy/move your std::function to the heap and return it as an opaque pointer. 一种这样的策略是将std::function复制/移动到堆中,并将其作为不透明指针返回。 Then, you would provide another function through your C++ interface that takes that opaque pointer and calls the function that it contains. 然后,您将通过C ++接口提供另一个函数,该函数采用该不透明指针并调用其中包含的函数。

// C side
struct function_opaque;
int call_opaque(struct function_opaque*, int param);

// C++ side
extern "C" {
    struct function_opaque {
        std::function<int(int)> f;
    };

    int call_opaque(function_opaque* func, int param) {
        return func->f(param);
    }
};

Of course, this comes with memory management implications. 当然,这带有内存管理的含义。

You need to put the typedef inside the extern "C" block at the minimum (to get it to compile as C++). 您需要至少将typedef放在extern "C"块中(以使其编译为C ++)。 I'm not sure that will work from C, however. 我不确定这是否可以在C中使用。 What will work from C is just to use plain function pointers, eg 在C中工作的只是使用普通的函数指针,例如

extern "C" {
using AdderFunction = int(int);
// old-style: typedef int(*AdderFunction)(int);
}

Edit: If you're using an API that gives you std::function objects, you can use the std::function::target() method to obtain the (C-callable) raw function pointer it refers to. 编辑:如果您使用的API可以为您提供std::function对象,则可以使用std::function::target()方法获取其引用的(C可调用)原始函数指针。

using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
        return makeAdder(amount).target<CAdderFunction>();
}
}

Another solution is to split the std::function into a pointer to the closure and a pointer to the member function, and pass three things to the C function that wants to invoke the lambda: 另一个解决方案是将std::function拆分为一个指向闭包的指针和一个指向成员函数的指针,并将三件事传递给要调用lambda的C函数:

  • The address of a C++ function that knows how to invoke the function on the closure type-safely 知道如何安全地在闭包上调用该函数的C ++函数的地址
  • The closure pointer (unsafely cast to void * ) 闭包指针(不安全地转换为void *
  • The member function pointer (hidden inside a wrapper struct and cast to void * as well) 成员函数指针(隐藏在包装结构中,并强制转换为void *

Here's a sample implementation. 这是一个示例实现。

#include <functional>
#include <iostream>

template<typename Closure, typename Result, typename... Args>
struct MemberFunctionPointer
{
    Result (Closure::*value)(Args...) const;
};

template<typename Closure, typename Result, typename... Args>
MemberFunctionPointer<Closure, Result, Args...>
member_function_pointer(
    Result (Closure::*const value)(Args...) const)
{
    return MemberFunctionPointer<Closure, Result, Args...>{value};
}

template<typename Closure, typename Result, typename... Args>
Result
call(
    const void *const function,
    const void *const closure,
    Args... args)
{
    return
        ((reinterpret_cast<const Closure *>(closure))
        ->*(reinterpret_cast<const MemberFunctionPointer<Closure, Result, Args...>*>(function)->value))
        (std::forward<Args>(args)...);
}

Sample usage from the C side: C端的用法示例:

int
c_call(
    int (*const caller)(const void *, const void *, int),
    const void *const function,
    const void *const closure,
    int argument)
{
    return caller (function, closure, argument);
}

Sample usage from the C++ side: C ++方面的用法示例:

int
main()
{
    int captured = 5;
    auto unwrapped = [captured] (const int argument) {
        return captured + argument;
    };
    std::function<int(int)> wrapped = unwrapped;

    auto function = member_function_pointer(&decltype(unwrapped)::operator());
    auto closure = wrapped.target<decltype(unwrapped)>();
    auto caller = &call<decltype(unwrapped), int, int>;
    std::cout
        << c_call(
            caller,
            reinterpret_cast<const void *>(&function),
            reinterpret_cast<const void *>(closure),
            10)
        << '\n';
}

The reason for the wrapper struct is that you can't cast a member function pointer to void * or any other object pointer type, not even with reinterpret_cast , so instead we pass the address of the member function pointer. 包装器结构的原因是您不能将成员函数指针强制转换为void *或任何其他对象指针类型,即使使用reinterpret_cast也不行,因此,我们传递成员函数指针的地址 You can choose to place the MemberFunctionPointer structure on the heap, eg with unique_ptr , if it needs to live longer than it does in this simple example. 您可以选择将MemberFunctionPointer结构放置在堆上,例如,使用unique_ptr ,如果该结构的生存期比此简单示例中的生存期更长。

You can also wrap these three arguments in a single structure on the C side, rather than pass them individually: 您还可以将这三个参数包装在C端的单个结构中,而不是分别传递它们:

struct IntIntFunction
{
    int (*caller)(const void *, const void *, int);
    const void *function;
    const void *closure;
};

#define INVOKE(f, ...) ((f).caller((f).function, (f).closure, __VA_ARGS__))

int
c_call(IntIntFunction function)
{
    return INVOKE(function, 10);
}

The problem with this solution is when you call makeAdder with parameter values.. Couldn't solve it but I'm posting just in case someone else can.. 此解决方案的问题是当您使用参数值调用makeAdder时。无法解决,但我发布的目的是为了防止其他人使用。

template <typename FunctionPointerType, typename Lambda, typename ReturnType, typename ...Args>
inline FunctionPointerType MakeFunc(Lambda&& lambda, ReturnType (*)(Args...))
{
    thread_local std::function<ReturnType(Args...)> func(lambda);

    struct Dummy
    {
        static ReturnType CallLambda(Args... args)
        {
            return func(std::forward<Args>(args)...);
        }
    };

    return &Dummy::CallLambda;
}

template <typename FunctionPointerType, typename Lambda>
FunctionPointerType MakeFunc(Lambda&& lambda)
{
    return MakeFunc<FunctionPointerType, Lambda>(std::forward<Lambda>(lambda), FunctionPointerType());
}


typedef int(*AdderFunction)(int);

AdderFunction makeAdder(int amount) {
    return MakeFunc<int(*)(int)>([amount] (int n) {
        return n + amount;
    });
}

extern "C" {
    typedef int(*CAdderFunction)(int);

    CAdderFunction makeCAdder(int amount)
    {
        return makeAdder(amount);
    }
}

It works by storing the lambda a thread local std::function . 它通过将lambda存储在线程本地std::function Then return a pointer to a static function which will call the lambda with the parameters passed in. 然后返回一个指向静态函数的指针,该函数将使用传入的参数调用lambda。

I thought about using an unordered_map and keeping track of each makeAdder call but then you can't reference it from static context.. 我考虑过使用unordered_map并跟踪每个makeAdder调用,但是您不能从静态上下文中引用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM