我目前正在尝试扩展sqrat(松鼠绑定工具),以满足将lambda绑定到松鼠的需要。
我遇到的问题是虽然存储和调用代码可以知道lambda的签名(这是设置执行lambda的函数的代码)
// Arg Count 0
template <class R, class Args, class Arity>
SQFUNCTION SqMemberLambdaFuncDetail(R result,
Args args,
boost::mpl::integral_c<unsigned int, 1> arity)
{
return &SqLambda<R>::template Func0<false>;
}
// Arg Count 1
template <class R, class Args, class Arity>
SQFUNCTION SqMemberLambdaFuncDetail(R result,
Args args,
boost::mpl::integral_c<unsigned int, 2> arity)
{
return &SqLambda<R>::template Func1<boost::mpl::at_c<Args, 1>::type, 2, false>;
}
template <class F>
SQFUNCTION SqMemberLambdaFunc(F f)
{
typedef boost::function_types::result_type<decltype(&F::operator())>::type result_t;
typedef boost::function_types::function_arity< decltype(&F::operator())> arity_t;
typedef boost::function_types::parameter_types< decltype(&F::operator())>::type args_t;
result_t result;
args_t args;
arity_t arity;
return SqMemberLambdaFuncDetail<result_t, args_t, arity_t>(result, args, arity);
}
lambda本身需要以匿名形式存储,作为二进制数据,我似乎无法找到一种方法。
答案 0 :(得分:3)
Lambda不保证是标准布局(所以看看它们的位是不合法的),它们的内容也不是内省的。
如果您想要可以序列化然后远程运行的代码,请使用脚本引擎。有很多,有些与C ++很好地互动。
或者只是提升凤凰,这是一个可以反映的C ++ - esque代码,它是lambda-esque。
但是,如果您唯一的问题是存储带有签名R(Args...)
的lambda的副本,只需使用std::function<R(Args...)>
。
答案 1 :(得分:2)
不幸的是,C ++ by design在代码和数据之间构建了一个难以穿透的墙。
Lambda是代码(加上捕获的数据),不能被视为数据(例如存储在磁盘上,通过网络发送,检查)。 C ++允许您将数据指向代码,而不是仅构建为编译时和不可变的代码本身。此外,指针仅在同一个程序中有效(发送指向其他程序的指针是没有意义的,因为它仅在特定的地址空间中有意义。)
唯一的可移植出路是实现(或合并)一种完整的编程语言,允许在运行时创建自定义代码。
答案 2 :(得分:2)
您可以创建将自己存储lambda的LambdaWrapper类(例如,在boost::function
或std::function
中)。这将是一个普通的课程,所以你应该像普通课一样把它传递给松鼠。
答案 3 :(得分:0)
您要做的是在脚本语言中分配一些内存,在其中存储C ++对象,然后再访问它。
您面临的一般问题是:
按照C ++的规则存储对象。
取回它。
给定某种类型T
,如果该类型是可复制的或可移动的,您可以随时复制/移动构造到一个足够大的存储T
并且正确对齐的分配。代码非常简单:
void func(T t)
{
void *alloc = GetAllocation(sizeof(T), alignof(T));
new(alloc) T(t);
}
其中GetAllocation
是您在脚本系统中分配内存所需要做的事情。展示位置new
语法将构造T
类型的对象,在这种情况下,通过从t
进行复制。你也可以移动:new(alloc) T(std::move(t));
。
哦,你还需要做你的脚本系统需要做的任何体操,这样一旦脚本完成,这个对象就会得到它的析构函数。这通常涉及为该特定对象的脚本系统注册某种功能。但是破坏对象会稍微进入第2步。
但这很容易。困难的部分是接收这些数据并再次使用它。例如,如果一个脚本试图调用这个函数,你将不得不附加一些钩子,让你的代码知道它可以调用它。当发生这种情况时,它必须重新构造T
类型的构造值。
问题在于:您实际上并不知道T
是什么类型。
如果您的函数传递了lambda,则无法知道它是什么类型。哦,您可以使用decltype
来访问该类型。但这只是因为注册函数传递的是该类型的值才开始。
在接收端,使用您的脚本语言,您所拥有的只是void*
。并且C ++无法记录您直接使用的类型。
当然,C ++确实有隐藏类型的方法,以便您以后可以重新构建它。这通常称为类型擦除:您有一种类型可以包含多种类型的值,只要它们具有特定的接口即可。这将允许您在不知道其类型的情况下与值进行交互。
std::function
是函数对象的类型。它可以采用任何可调用的类型:函数指针,成员指针,仿函数和lambdas(aka:functors)。由于调用它是你想要的,这应该是一个可接受的类型擦除对象。
只要您知道每个注册功能将注册哪个签名。毕竟,它携带的函数的签名是std::function
类型的一部分。因此,您必须将这些仿函数标准化为特定的签名,该签名在仿函数在脚本系统中注册时已知。
您可以使用不同的功能注册不同的签名。的确,你可以制作这样的功能模板。但是你仍然需要知道一个特定的功能被注册它的签名是什么。
这对于在GC时间销毁对象也很重要,因为你需要能够将它强制转换。