【问题标题】:serializing a large struct without "save" methods序列化没有“保存”方法的大型结构
【发布时间】:2017-11-21 11:17:11
【问题描述】:

我有一个大型配置结构,它由子结构、int、char、long 和字符串组成。子结构也是由相同的元素构建的。

此配置结构需要序列化并在集群上的节点之间发送。

我一直在寻找一种方法来序列化它,而无需在每个结构中创建“保存”方法,如谷物和 boost 库想要的。代码在几个程序员之间共享,结构非常大。恐怕有人会更新结构或其子结构之一而忘记相应地更新“保存方法”

想法?

** 在 cpp 中

【问题讨论】:

  • 二进制表示是一种选择吗?
  • 我担心有人会更新结构或其子结构之一而忘记相应地更新“保存方法 - 这就是测试的目的。如果您的配置可以很好地与 == 进行比较,然后您可以序列化、反序列化,然后进行比较和断言。
  • @StephanLechner 二进制表示是一个选项,我不在乎它是如何序列化的

标签: c++ serialization


【解决方案1】:

如果您可以确定集群中的所有节点都具有相同的架构并执行相同的编译代码,那么您实际上可以以二进制模式传输数据,即作为对象的“原始内存转储”。需要相同的架构/相同的编译代码,因为不同的架构和/或不同的编译器可能导致所使用的数据类型的大小不同以及不同的“填充”。然后,发送方和接收方使用的内存布局可能不同,导致数据混乱。

进一步注意,您的“结构”(或类)必须是 POD 类型,即没有虚拟构造函数/析构函数,没有虚拟成员函数,这样您就可以使用“内存转储”对其进行初始化。

如果您了解这些限制,请参阅以下代码,该代码通过以二进制模式写入和读取文件来“模拟”传输。调整此代码以使用您选择的通道实际传输代码,例如插座。

希望对你有帮助。

#include <fstream>
#include <iostream>

using namespace std;

struct myStruct {
    int x;
    int y;
    char name[10];
    double z;

    void printOnConsole() {
        cout << "x:" << x << ";y:" << y << ";name:"<< name << ";z:" << z << endl;
    }
    void writeBinary(ofstream &out) {
        out.write((char*)this, sizeof(*this));
    }
    bool readBinary(ifstream &in) {
        in.read((char*)this, sizeof(*this));
        return in.gcount() == sizeof(*this);
    }
};


int main()
{
    myStruct myStructObjs[] = {
        { 10, 20, "Herbert", 3.5 },
        { 30, 40, "Anton", 4.6 }
    };
    cout << "Objects to be transferred:" << endl;
    myStructObjs[0].printOnConsole();
    myStructObjs[1].printOnConsole();

    cout << "Simulating transfer:" << endl;
    ofstream send("data.bin", ios_base::binary | ios_base::out);
    if (send) {
        myStructObjs[0].writeBinary(send);
        myStructObjs[1].writeBinary(send);
        send.close();
        cout << "Two objects transferred." << endl;
    }
    else {
        cout << "Error 'sending' data." << endl;
        return 1;
    }


    cout << "Simulating receive:" << endl;
    ifstream receive("data.bin", ios_base::binary);
    if (receive) {
        myStruct receivedObj;
        int n = 0;
        while (receivedObj.readBinary(receive)) {
            receivedObj.printOnConsole();
            n++;
        }
        cout << n << " objects received." << endl;
    }
    else {
        cout << "Error 'receiving' data." << endl;
        return 1;
    }

    return 0;
}

【讨论】:

  • 我的结构不是 POD,因为它有字符串,但在阅读之后似乎最好将它们设为 char[] 并按照您的建议进行操作。我应该提醒其他人,如果他们使用 cpp 14,他们可以使用 boost hana 轻松遍历结构和序列化
【解决方案2】:

Boost 提供了一种非侵入式的序列化方式,see here 在“非侵入式版本”下

【讨论】:

  • 鉴于 OP 已经表示他们知道提升,我认为这个答案不会增加太多。
  • 他的问题暗示他认为他必须使用 boost 将 save 方法放入类中。使用非侵入式版本并非如此。
  • @mattideluxe 问题不在于天气是否将存档放入其中。问题是开发人员可能会不小心忘记在两个地方输入新值
  • @Epic & UKMonkey:你说得对……我想我跳过了问题的最后一部分……抱歉
【解决方案3】:

C++ 中没有反射。 QT 和虚幻引擎正在使用它们自己的编译步骤来为此目的创建代码反射。您也可以查看 cpp3k 这是一个 clang 扩展。

我可以向您建议一种方法,以确保在保存功能中没有适当更新的结构修改会很明显:​​

struct myData {
int32_t a;
uint8_t b;
};

void save(const myData& data) {
    save(data.a);
    save(data.b);
    assert(sizeof(myData) == 5); // When adding new elements to myData make sure to update this function
}

附:确保对齐不会影响您的尺寸。


更新。

这个库可能会帮助你magic_get。 这两篇文章也很有意思An Introduction to Reflection in C++,Fun with Reflection in C++

UPD.2

您可能想查看protocol buffers 或类似的库。

【讨论】:

  • 我在您的链接中看到了 boost hana。这似乎允许遍历结构成员。我不能使用它,因为它需要 cpp 14 但它可能对某人有用
【解决方案4】:

恐怕有人会更新结构或其子结构之一而忘记相应地更新“保存方法”

放一个怎么样

McCfgStruct::serialize(...) {
  std::static_assert(sizeof(MyCfgStruct) == 32);
}

(或等价物)这样,如果有人将新变量添加到结构中并且没有更新序列化方法的预期大小,您将收到编译器错误。

【讨论】:

    猜你喜欢
    • 2022-10-23
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 2015-03-21
    • 2019-05-03
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    相关资源
    最近更新 更多