是的,用于函数指针和闭包。不为std::function
。
函数指针是最简单的——它只是一个和其他指针一样的指针,因此您可以将其读取为字节:
template <typename _Res, typename... _Args>
std::string serialize(_Res (*fn_ptr)(_Args...)) {
return std::string(reinterpret_cast<const char*>(&fn_ptr), sizeof(fn_ptr));
}
template <typename _Res, typename... _Args>
_Res (*deserialize(std::string str))(_Args...) {
return *reinterpret_cast<_Res (**)(_Args...)>(const_cast<char*>(str.c_str()));
}
但令我惊讶的是,即使不重新编译,函数的地址也会在程序的每次调用时发生变化。如果您想传输地址,则不是很有用。这是由于ASLRyour_program
造成的,您可以在 Linux 上以.开头将其关闭setarch $(uname -m) -LR your_program
。
现在您可以将函数指针发送到运行相同程序的不同机器,然后调用它!(这不涉及传输可执行代码。但除非您在运行时生成可执行代码,否则我认为您不会在寻找那个。)
lambda 函数完全不同。
std::function<int(int)> addN(int N) {
auto f = [=](int x){ return x + N; };
return f;
}
的值f
将是捕获的int N
. 它在内存中的表示与int
! 编译器为 lambda 生成一个未命名的类,它f
是一个实例。这个类已经operator()
用我们的代码重载了。
未命名的类给序列化带来了问题。它还提出了从函数返回 lambda 函数的问题。后一个问题由std::function
.
std::function
据我了解是通过创建一个模板化的包装类来实现的,该类通过模板类型参数有效地保存对 lambda 函数背后的未命名类的引用。(这是_Function_handler
在函数式中。)std::function
接受一个指向_M_invoke
这个包装类的静态方法()的函数指针,并存储它加上闭包值。
不幸的是,所有内容都隐藏在private
成员中,并且没有存储闭包值的大小。(它不需要,因为 lambda 函数知道它的大小。)
所以std::function
不适合序列化,但可以很好地作为蓝图。我遵循它的做法,对其进行了很多简化(我只想序列化 lambda,而不是无数其他可调用的东西),将闭包值的大小保存在 a 中size_t
,并添加了用于(反)序列化的方法。有用!