我有一些返回std::function
的C ++代码。我想从一些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);
}
}
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
我理解为什么会发生这种情况,但想知道是否有一种模式可以实现这一目标?
答案 0 :(得分:16)
C / C ++之间最便携的接口方法是使用指针在语言之间传递数据,并使用非成员函数进行函数调用。
.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
在.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;
}
答案 1 :(得分:8)
无法从C调用std::function
,因为C不支持所需的语言功能。 C没有模板,访问修饰符,可调用对象,虚拟方法或std::function
可以在引擎盖下使用的任何其他内容。你需要提出一个C可以理解的策略。
其中一种策略是将std::function
复制/移动到堆中并将其作为不透明指针返回。然后,您将通过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);
}
};
当然,这会带来对内存管理的影响。
答案 2 :(得分:4)
您需要将typedef放在extern "C"
块中最小(以使其以C ++编译)。但是,我不确定它是否会起作用。从C开始的工作只是使用普通函数指针,例如
extern "C" {
using AdderFunction = int(int);
// old-style: typedef int(*AdderFunction)(int);
}
编辑:如果您正在使用提供std::function
个对象的API,则可以使用std::function::target()
方法获取它引用的(C-callable)原始函数指针。
using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
return makeAdder(amount).target<CAdderFunction>();
}
}
答案 3 :(得分:1)
另一种解决方案是将std::function
拆分为指向闭包的指针和指向成员函数的指针,并将三个内容传递给想要调用lambda的C函数:
void *
)void *
)这是一个示例实现。
#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)...);
}
来自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);
}
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';
}
包装器结构的原因是你不能将成员函数指针转换为void *
或任何其他对象指针类型,甚至不能使用reinterpret_cast
,所以我们传递成员函数指针的地址。您可以选择将MemberFunctionPointer
结构放在堆上,例如使用unique_ptr
,如果它需要比这个简单示例中的寿命更长。
您也可以将这三个参数包装在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);
}
答案 4 :(得分:0)
此解决方案的问题是当您使用参数值调用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);
}
}
它的工作原理是将lambda存储为本地线程std::function
。然后返回一个指向静态函数的指针,该函数将使用传入的参数调用lambda。
我考虑过使用unordered_map
并跟踪每个makeAdder
调用,但是您无法从静态上下文中引用它。