将数据序列化代码从 C++ linux/mac 移植到 C++ windows

Posted

技术标签:

【中文标题】将数据序列化代码从 C++ linux/mac 移植到 C++ windows【英文标题】:Porting data serialization code from C++ linux/mac to C++ windows 【发布时间】:2011-11-16 15:48:57 【问题描述】:

我有一个在 mac 和 linux 上编译并成功运行的软件框架。我现在正在尝试将其移植到 Windows(使用 mingw)。到目前为止,我已经在 Windows 下编译和运行了该软件,但它不可避免地存在错误。特别是,我在将 macos(或 linux)中序列化的数据读取到程序的 windows 版本(segfaults)时遇到问题。

序列化过程将原始变量(long、int、double 等)的值序列化到磁盘。

这是我正在使用的代码:

#include <iostream>
#include <fstream>

template <class T>
void serializeVariable(T var, std::ofstream &outFile)

    outFile.write (reinterpret_cast < char *>(&var),sizeof (var));


template <class T>
void readSerializedVariable(T &var, std::ifstream &inFile)

inFile.read (reinterpret_cast < char *>(&var),sizeof (var));

所以为了保存一堆变量的状态,我依次为每个变量调用serializeVariable。然后为了读回数据,以与保存数据相同的顺序调用 readSerializedVariable。例如保存:

::serializeVariable<float>(spreadx,outFile);
::serializeVariable<int>(objectDensity,outFile);
::serializeVariable<int>(popSize,outFile);

阅读:

::readSerializedVariable<float>(spreadx,inFile);
::readSerializedVariable<int>(objectDensity,inFile);
::readSerializedVariable<int>(popSize,inFile);

但在 Windows 中,这种序列化数据的读取失败。我猜windows对数据的序列化有点不同。我想知道是否有一种方法可以修改上述代码,以便可以在任何其他平台上读取保存在任何平台上的数据......有什么想法吗?

干杯,

本。

【问题讨论】:

什么是T类型?我可以为不同的编译器考虑不同的 sizeof(var) 值——例如,作为结构元素对齐的结果。想想另一种序列化机制,比如 XML。 类型是原始变量(例如,参见编辑后的帖子)..例如,我不知道编译器可以为 sizeof(long) 生成不同的值。你确定吗?我的意思是,我认为 long 总是 4 个字节,char 总是 1 个字节,独立于编译器等。 是的,绝对是,我们处理多个平台,在一些平台上 long 是 4 个字节,在一些平台上是 8 个字节。我相信标准只规定了数据类型的最小字节数,而不是确切的字节数。 既然如此,混蛋!谢谢!将按照 Alex 的建议研究 XML。刚刚确认这是问题所在:在我的 Mac 上,long 是 8 个字节,在我的 Windows 平台上,它是 4。 您可能还想研究 boost 的序列化库或 json(我认为是 libjanson),因为我发现它们的重量更轻。 【参考方案1】:

像这样的二进制序列化应该可以在这些平台上正常工作。您确实必须尊重字节顺序,但这是微不足道的。我认为这三个平台在这方面没有任何冲突。

不过,你真的不能使用松散的类型规范。 intfloatsize_t 大小都可以跨平台变化。

对于整数类型,请使用在 cstdint 标头中找到的严格大小的类型。 uint32_tint32_t 等。Windows 没有可用的 iirc 标头,但您可以改用 boost/cstdint.hpp。

浮点应该工作,因为大多数编译器都遵循相同的 IEEE 规范。

C - Serialization of the floating point numbers (floats, doubles)

二进制序列化确实需要彻底的单元测试。我强烈建议您投入时间。

【讨论】:

谢谢汤姆。我真的想弄清楚我的实施发生了什么。修复后会更新。 :-) 愚蠢地,正如 user103749 所建议的那样,由于某种原因,我在打开文件流时忘记了合并 std::ios::binary 。由于不打扰这个总是在 linux 和 mac 下工作,所以这个错误就溜走了。很高兴我坚持使用这种序列化方法。干杯。【参考方案2】:

这只是一个疯狂的猜测,我无法为您提供更多帮助。我的想法是字节顺序不同:大端与小端。因此,任何大于一个字节的内容在加载到顺序颠倒的机器上时都会被弄乱。

例如,我在 msdn 中发现了这种和平的代码:

int isLittleEndian() 
    long int testInt = 0x12345678;
    char *pMem;

    pMem = (char *) testInt;
    if (pMem[0] == 0x78)
        return(1);
    else
        return(0);

我猜你在 linux 和 windows 上会有不同的结果。最好的情况是,如果您的编译器有一个标志选项可以使用一种格式或另一种格式。只需将其设置为在所有机器上都相同即可。

希望这会有所帮助, 亚历克斯

【讨论】:

谢谢亚历克斯,我将运行一些测试来确定我不同平台的字节顺序.. 好吧,我的 mac 机器和 windows 平台上的字节顺序似乎完全相同,所以我怀疑字节顺序与它有什么关系。不过还是谢谢.. 丹也可能是对的,这就是问题所在。特别是 int 不必在通常的 4 个字节附近。不幸的是,我没有任何第一手经验。顺便提一句。阅读失败到底是什么意思?对于任何数据类型?错误的值或错误? 谢谢@Fritz -- 我还在调试代码,想知道到底发生了什么......当我弄清楚时会更新......【参考方案3】:

还有一个疯狂的猜测: 您忘记了以二进制读取模式打开文件,以及在 Windows 文件流上打开文件 将序列 13,10 转换为 10。

【讨论】:

我很抱歉,但我完全不明白你的意思。你能澄清一下吗? 我的意思是你在windows上打开文件流时使用std::ios::binary之类的东西吗? 感谢您的建议。我意识到在一些地方,我确实忘记了使用 std::ios::binary。我对此感到有点傻,但现在一切似乎都很好,并且序列化按预期工作:-)【参考方案4】:

您是否考虑过使用序列化库或格式,例如:

XDR(由 libc 支持)或 ASN1 s11n(一个 C++ 序列化库) Json,一种非常简单的文本格式,包含许多库,例如JsonCpp, Jansson, Jaula, ....) YAML,一种更强大的文本格式,包含许多库 甚至XML,通常用于序列化目的...

(对于标量的序列化,htonl 和配套例程应该会有所帮助)

【讨论】:

感谢您的建议——我没有听说过这些想法,除了 XML...感谢您打开我的眼睛。我仍然会尝试我首先做的方式,因为在我看来它应该可以工作......当我修复时会更新:-)

以上是关于将数据序列化代码从 C++ linux/mac 移植到 C++ windows的主要内容,如果未能解决你的问题,请参考以下文章

C#搞跨平台UI,封装Cef作为Cpf的控件支持Windows,Linux,Mac

从数组循环左移问题中浅谈考研算法设计的规范代码

如何从非托管 C++ 代码获取结构化列表值到 C#?

分段错误:C++ 中的结构序列化和 MPI 数据传输

如何使用 boost_python 将 C++ 序列化数据公开给 python

如何将 C++ 本机对象编组到托管 C++ CLI