I have some C++ code that returns a std::function
. I would like to call this from some C code. Is this possible? As an example I have the following code:
我有一些c++代码返回std::函数。我想从C代码调用它。这是可能的吗?例如,我有以下代码:
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:
铿锵声+ +化= c + + 11测试。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?
我理解为什么会发生这种情况,但我想知道是否有一种模式可以使它成为可能?
5 个解决方案
#1
16
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;
}
#2
8
It's not possible to call a std::function
from C, because C doesn't support the language features that are required. C doesn't have templates, access modifiers, callable objects, virtual methods, or anything else that std::function
could use under the hood. You need to come up with a strategy that C can understand.
不可能从C调用std::函数,因为C不支持所需的语言特性。C没有模板、访问修饰符、可调用对象、虚拟方法或任何std:::函数可以在引擎盖下使用的东西。你需要想出一个C可以理解的策略。
One such strategy is to copy/move your std::function
to the heap and return it as an opaque pointer. Then, you would provide another function through your C++ interface that takes that opaque pointer and calls the function that it contains.
其中一个策略是复制/移动std::对堆的函数,并将其作为一个不透明的指针返回。然后,通过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.
当然,这与内存管理有关。
#3
4
You need to put the typedef inside the extern "C"
block at the minimum (to get it to compile as C++). I'm not sure that will work from C, however. What will work from C is just to use plain function pointers, e.g.
您需要将typedef至少放在extern“C”块内部(使其编译为c++)。不过,我不确定C是否适用。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:::函数对象,您可以使用std:::::target()方法来获取它引用的(C-callable)原始函数指针。
using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
return makeAdder(amount).target<CAdderFunction>();
}
}
#4
1
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::函数拆分为指向闭包的指针和指向成员函数的指针,并将三件事传递给要调用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 *
) - 闭包指针(不安全的cast到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. You can choose to place the MemberFunctionPointer
structure on the heap, e.g. with unique_ptr
, if it needs to live longer than it does in this simple example.
包装器结构的原因是,您不能将成员函数指针转换为void *或任何其他对象指针类型,即使使用reexplain cast,也不能,因此我们传递成员函数指针的地址。您可以选择将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);
}
#5
0
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
. Then return a pointer to a static function which will call the lambda with the parameters passed in.
它的工作方式是存储lambda线程本地std::函数。然后返回一个指向静态函数的指针,该函数将调用带有传入参数的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调用,但是不能从静态上下文引用它。
#1
16
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;
}
#2
8
It's not possible to call a std::function
from C, because C doesn't support the language features that are required. C doesn't have templates, access modifiers, callable objects, virtual methods, or anything else that std::function
could use under the hood. You need to come up with a strategy that C can understand.
不可能从C调用std::函数,因为C不支持所需的语言特性。C没有模板、访问修饰符、可调用对象、虚拟方法或任何std:::函数可以在引擎盖下使用的东西。你需要想出一个C可以理解的策略。
One such strategy is to copy/move your std::function
to the heap and return it as an opaque pointer. Then, you would provide another function through your C++ interface that takes that opaque pointer and calls the function that it contains.
其中一个策略是复制/移动std::对堆的函数,并将其作为一个不透明的指针返回。然后,通过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.
当然,这与内存管理有关。
#3
4
You need to put the typedef inside the extern "C"
block at the minimum (to get it to compile as C++). I'm not sure that will work from C, however. What will work from C is just to use plain function pointers, e.g.
您需要将typedef至少放在extern“C”块内部(使其编译为c++)。不过,我不确定C是否适用。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:::函数对象,您可以使用std:::::target()方法来获取它引用的(C-callable)原始函数指针。
using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
return makeAdder(amount).target<CAdderFunction>();
}
}
#4
1
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::函数拆分为指向闭包的指针和指向成员函数的指针,并将三件事传递给要调用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 *
) - 闭包指针(不安全的cast到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. You can choose to place the MemberFunctionPointer
structure on the heap, e.g. with unique_ptr
, if it needs to live longer than it does in this simple example.
包装器结构的原因是,您不能将成员函数指针转换为void *或任何其他对象指针类型,即使使用reexplain cast,也不能,因此我们传递成员函数指针的地址。您可以选择将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);
}
#5
0
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
. Then return a pointer to a static function which will call the lambda with the parameters passed in.
它的工作方式是存储lambda线程本地std::函数。然后返回一个指向静态函数的指针,该函数将调用带有传入参数的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调用,但是不能从静态上下文引用它。