-->

序列化对象的功能(Serializing function objects)

2019-06-28 01:24发布

是否有可能进行序列化和反序列化std::function ,函数对象,或一般在C ++封闭? 怎么样? 难道C ++ 11利于呢? 是否有任何库支持可用于这样的任务(例如,在升压)?

例如,假设一个C ++程序具有std::function这是需要被传送(通过TCP / IP套接字说)驻留另一台机器上的另一C ++程序。 你怎么在这种情况下建议?


编辑:

为了澄清,其是要移动的功能应该是纯和自由侧效果的。 所以,我没有安全或国家不匹配的问题。

一个解决问题的办法是建立一个小型的嵌入式领域特定语言和序列化的抽象语法树。 我希望我能找到的移动功能的机器无关的表示,而不是一些语言/库支持。

Answer 1:

没有。

C ++有没有内置用于序列化支持,并从未与发送从一个过程到另一代码的思想设想,以免一台机器到另一台。 可以这样做通常语言功能和反射两者的IR(也就是独立于机器的代码的中间表示)。

所以,你留下了自己写一个协议,用于传输所需的行动和DSL的方法是可行的肯定...根据品种要执行的任务和需要的性能。

另一个解决方案是去与现有的语言。 例如Redis的NoSQL的数据库嵌入一个LUA引擎,并可以执行Lua脚本,你可以做同样的和在网络上传输LUA脚本。



Answer 2:

是的函数指针和封锁。 不是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上启动关闭your_programsetarch $(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 ! 编译器生成的拉姆达,其中一个未命名的类f是一个实例。 这个类有operator()与我们的代码超载。

类是未命名提出了序列化的问题。 这也提出了从功能恢复lambda函数的一个问题。 后一个问题被解决std::function

std::function ,据我了解是通过创建实际持有通过模板类型参数参考lambda函数背后的无名类模板化的包装类来实现。 (这是_Function_handler在功能 。) std::function需要一个函数指针一个静态方法( _M_invoke此包装类并存储加上封盖值的)。

不幸的是,一切都埋在private成员,而不是存储在截流值的大小。 (这并不需要,因为lambda函数知道它的大小。)

因此, std::function本身不适合系列化,但效果很好的蓝图。 我跟着它做什么,简化了很多(我只是想序列化lambda表达式,而不是无数其他的东西可赎回),在保存关闭值的大小size_t ,并增加了对(德)序列化方法。 有用!



Answer 3:

没有,但也有一些限制的解决方案。

你所能期待的最重要的是在某种全球地图(例如,用钥匙串)是常见的代码发送和接收代码(在不同的计算机上或之前,系列化后)注册功能。 然后,您可以用序列化功能相关的字符串,并得到它的另一面。

作为一个具体的例子库HPX实现了这样的事情,在一些所谓的HPX_ACTION 。

这需要大量的协议,并且它是脆弱的相对于在代码更改。

但毕竟这是没有什么东西,尝试序列一类的私有数据不同。 在某种意义上功能的代码是其私处(参数和返回接口是公共部分)。

是什么让你希望的滑移是取决于你如何组织这些“物”的代码可以是全局的或常见,如果一切顺利的权利,他们是序列化和反序列化通过某种预定的运行过程中间接获得。

这是一个粗例如:

串行代码:

// common:
class C{
  double d;
  public:
  C(double d) : d(d){}
  operator(double x) const{return d*x;}
};
C c1{1.};
C c2{2.};
std::map<std::string, C*> const m{{"c1", &c1}, {"c2", &c2}};
// :common

main(int argc, char** argv){
   C* f = (argc == 2)?&c1:&c2;
   (*f)(5.); // print 5 or 10 depending on the runtime args
   serialize(f); // somehow write "c1" or "c2" to a file
}

解串器的代码:

// common:
class C{
  double d;
  public:
  operator(double x){return d*x;}
};
C c1;
C c2;
std::map<std::string, C*> const m{{"c1", &c1}, {"c2", &c2}};
// :common

main(){
   C* f;
   deserialize(f); // somehow read "c1" or "c2" and assign the pointer from the translation "map"
   (*f)(3.); // print 3 or 6 depending on the code of the **other** run
}

(代码未测试)。

请注意,这迫使很多共同和一致的代码,但根据环境中,您可能能够保证这一点。 在代码丝毫的改变可产生难以察觉的逻辑错误。

另外,我在这里打与全局对象(可以自由函数中使用),但同样可以与范围内的对象做什么变得棘手的是如何建立本地地图( #include局部范围内通用的代码?)



文章来源: Serializing function objects