我想序列化一个函数,并将它发送到运行相同代码的不同进程(动态库)。我最初的方法是使用库谷物和std::function,但这种类型不受支持,有很多原因。
现在,我考虑使用lambda转换为函数指针的方法,但我不太确定我对它们的行为的理解是否正确。使用下面的代码,函数指针指向什么?如果它是一个静态函数,我假设我可以安全地将指针移动到另一个进程,并从那里调用它。
#include <iostream>
// Nice name for function type
using Foo = int(*)();
int main()
{
auto func = []() -> int
{
return 1;
};
// convert lambda to function pointer w/o captures
Foo fo = func;
// move (serialized) 'Foo fo' to different process
// ...
// calling function pointer in different process
std::cout << fo();
}这安全吗?如果没有,我怎样才能达到同样的目标呢?我可以回到原来的静态函数,跳过lambda,但是我喜欢lambda的组织,即lambda带给我的用例。
更新
当我使用模板添加函数作为模板参数,然后序列化类型时,可能会发生什么情况。
#include <iostream>
template<void(*F)()>
class SerializableObj
{
public:
void execute()
{
F();
}
};
void foo()
{
std::cout << "HI!";
}
int main()
{
// calling function pointer in different process
SerializableObj<foo> obj;
// serialize and move obj
// ...
// in other thread / process
obj.execute();
}在戈德波特中,execute()现在通过符号而不是通过函数地址调用函数。(据我所知)
发布于 2019-07-18 13:54:27
一个进程地址空间中指针的二进制值是另一个进程地址空间中的随机位。
动态库通常按字面上的随机地址(称为地址空间随机化)加载,即使不是,它们也被加载到动态地址(这可能碰巧是相同的地址,直到它们没有被加载,因为首先加载了另一个库)。
静态函数并不比lambdas更好。
您需要一个明确的函数表,确保两个进程的顺序相同,并将一个索引传递到该表中。
发布于 2019-07-18 13:59:55
正如其他答案所述,将std::function序列化为二进制代码以序列化任意函数是非常危险的选择。
如果您想通过网络向另一个应用程序实例发送一些函数,我建议您不要通过lambda或std::function来表示函数,而是使用某种语法树。这个表示可以在一边序列化,另一边反序列化,然后执行。
发布于 2019-07-22 22:58:40
您最好创建可调用的对象并序列化它们:
struct Callable {
virtual void execute() = 0;
};
class MyCallable : public Callable {
public:
void execute() override { std::cout << "HI! my data is " << x << std::end; }
// Some data to send along with your Callable
int x;
// Cereal serialization function.
template <class Archive>
void serialize( Archive & ar )
{
ar( x );
}
};
// Register your Callable type.
CEREAL_REGISTER_TYPE(MyCallable);您可能不需要这种多态性。
https://stackoverflow.com/questions/57095837
复制相似问题