是否可以在 C++ 中对 std::function
、函数对象或闭包进行序列化和反序列化?如何? C++11 是否促进了这一点?是否有任何库支持可用于此类任务(例如,在 Boost 中)?
例如,假设一个 C++ 程序有一个 std::function
需要与驻留在另一台机器上的另一个 C++ 程序进行通信(例如通过 TCP/IP 套接字)。在这种情况下你有什么建议?
编辑:
为了澄清,要移动的函数应该是纯的且无副作用的。所以我没有安全或状态不匹配的问题。
该问题的解决方案是构建一个小型嵌入式领域特定语言并将其抽象语法树序列化。 我希望我能找到一些语言/库支持来移动独立于机器的函数表示。
最佳答案
对于函数指针和闭包是。不适用于 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()));
}
但我惊讶地发现,即使没有重新编译,函数的地址也会在每次调用程序时发生变化。如果您想传输地址,则不是很有用。这是由于 ASLR ,您可以在 Linux 上通过使用 setarch $(uname -m) -LR your_program
启动 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 函数背后的未命名类的引用。 (这是 functional 中的 _Function_handler
。) std::function
采用指向此包装类的静态方法 (_M_invoke
) 的函数指针并存储它加上闭包值。
不幸的是,所有内容都隐藏在 private
成员中,并且没有存储闭包值的大小。 (它不需要,因为 lambda 函数知道它的大小。)
所以 std::function
不适合序列化,但可以很好地作为蓝图。我遵循它所做的,简化了很多(我只想序列化 lambda,而不是无数其他可调用的东西),将闭包值的大小保存在 size_t
中,并为 (de ) 序列化。有效!
关于c++ - 序列化函数对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12338265/