[英]Serialize Lambda Functions with Cereal
我想序列化一個函數並將其發送到運行相同代碼(動態庫)的不同進程。 我最初的方法是使用庫谷物和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帶給我的用例的組織。
UPDATE
當我使用模板將函數添加為模板參數然后序列化類型時可能會發生什么。
#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();
}
在godbolt中, execute()
現在通過符號調用函數,而不是通過函數地址調用。 (據我所理解)
一個進程地址空間中的指針的二進制值是另一個進程地址空間中的隨機位。
動態庫通常在字面上隨機地址(稱為地址空間隨機化)加載,即使它們不是,它們也被加載到動態地址(可能是相同的地址,直到它們不是因為首先加載了另一個庫) )。
靜態函數並不比lambdas好。
您需要一個明確的函數表,保證在兩個進程中保持相同的順序,並將索引傳遞給該表。
正如在其他答案中所說的那樣 - 將std :: function序列化為二進制代碼以序列化任意函數是一個非常危險的選擇。
如果你想通過網絡將一些函數發送到另一個應用程序實例,我建議你不要通過lambda或std :: function來表示函數,而是使用某種語法樹。 該表示可以在一側序列化,在另一側反序列化並執行。
你也可以制作可調用的對象並將它們序列化:
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);
您可能不需要多態性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.