是的函数指针和闭包。不适用于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,您可以通过使用setarch $(uname -m) -LR your_program
开始your_program
来关闭Linux。
现在您可以将函数指针发送到运行相同程序的其他机器,并将其调用! (这并不涉及传输可执行代码,但除非您在运行时生成可执行代码,否则我认为您不在此寻找。)
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
的functional。)std::function
将函数指针指向此包装类的静态方法(_M_invoke
),并将其存储并加上闭包值。
不幸的是,一切都埋在private
成员中,并且不存储闭包值的大小。 (不需要,因为lambda函数知道它的大小。)
因此std::function
不适合序列化,但作为蓝图很好。我遵循它的操作,简化了很多(我只想序列化lambda表达式,而不是其他无数可调用的东西),将关闭值的大小保存在size_t
中,并添加了(反)序列化方法。有用!
忘记它。查找“远程过程调用”的概念,以及它的流行实现。 –
不,不,不。任何这样的对象,当你剥离漂亮的类型安全包装时,都是指向某些机器代码的指针。您既不能发送机器代码也不能发送指向其他进程或其他机器的指针。 –
@ kerrek-sb我不确定RPC是否适合我的目的,在这里我有很多小函数对象在发送端不断构造和解构。以某种方式传输逻辑本身会更好。 – shaniaki