【问题标题】:Datastructure Storage in Filesystem文件系统中的数据结构存储
【发布时间】:2012-09-02 01:27:33
【问题描述】:

我正在尝试用 C++ 编写一个持久数据结构,但是我觉得我应该能够使它与我的数据结构阅读器的各种其他实现二进制兼容,因此,我目前的想法是在本机内存中声明数据结构没有任何抽象。

例如,我会指定一个线性内存块作为数据结构(使用new 关键字),然后描述第一个字节的含义,第二个字节的含义等等。我知道我可以使用struct 来做到这一点,但是随后,数据结构将绑定到一种语言,而其他语言则必须使用此结构。此外,实现可能会从编译器更改为编译器。相反,我希望它作为内存标准。

我正在尝试做的事情有点明智吗?或者我试图过度简化事情并且真的应该使用struct 数据结构?现在进入 C++ 部分,如果您认为我应该使用 struct 数据结构,那么使用成熟类有什么缺点?

(无论如何,我都在使用一个类来包装内存结构并为其提供函数,因为数据结构无论如何都是持久的。) 编辑 正如贾斯汀所建议的那样,我需要围绕内存结构的任何此类高级接口包装器,因此我关于类包装器的最后一点没有正确说明。我的意思是我想要一个用于内存表示的类接口,它不一定是包装器。

【问题讨论】:

    标签: c++ c binary-compatibility


    【解决方案1】:

    我已经阅读/使用过的几种文件格式正是这样做的——定义内存标准或布局,然后通常用类似 C 的伪structure 中的演示来支持它。有时它们会提供结构或类表示,有些则完全由库抽象。当然,这些格式会继续记录所有字段、它们的大小、数据的字节顺序等等。

    我认为与字节序相关的问题、填充、复杂性(例如由数据结构的变化引入)和正确的版本控制是最大的错误来源。我发现的另一个问题是过去的数据结构的使用以及用于表示类似功能的数据结构的不一致——你可能会收到一份规范,并意识到它包含几种不同的字符串表示——所有这些都是过时的,有人必须继续支持所有这些(双向)。

    继续那条路线:

    如果您不想支持二进制表示(或可编译程序)(并且随着平台和工具集的变化,尝试长期存在的格式在此过程中失败/绊倒),则不应提交二进制表示(或可编译程序)。只需首先提交正式的内存标准,然后在此基础上构建测试和示例输入文件,以验证表示是否正确序列化和反序列化。一个非常基本的测试套件将有助于确保您的模型在您需要的所有系统上都是可移植的,并且可以指出您可能没有意识到的潜在缺陷或特定于平台的注意事项。

    如果您真的想提供可编译的表示,我会坚持使用非常合规的struct 表示——客户端可以采用该(在内存中)表示并将其转换为他们喜欢的任何 C++ 抽象/表示。也就是说,序列化的表示可能不应该反映内存中的表示,除了琐碎的简单表示和这种表示的中间存储(扁平化和打包结构)。

    其中一个重要部分是您应该进行测试,以确认您使用这些结构创建的内存对象图是可向前和向后可序列化和可反序列化的,并支持正确的版本控制——因此通常需要一些努力使复杂的序列化表示兼容。所以你看到这种方法只是在另一个之上引入了一个抽象层。在这方面,您可能希望 C++ 抽象能够从内存中的打包表示创建自己,并确保该表示也可以正确填充打包结构而不会丢失数据。

    除此之外,是否有任何需要拥有更高级的界面?如果有,那么您可能需要提供该信息。

    所以是的,内存标准是您必须正确和稳定的部分,所有实现都应该参考和测试它——无论平台/架构差异如何。 IOW,你走在正确的轨道上;)

    【讨论】:

    • 不,除了您所描述的之外,没有任何需要拥有更高级的界面。
    【解决方案2】:

    在 C++ 中,structclass 之间没有实际区别(除了 struct 中的默认可访问性是公共的)。传统上,struct 用于一个类型只有(公共)成员变量而没有成员函数,但这只是一个约定,而不是编译器强制执行的规则。

    我当然会使用struct/class 来描述数据。如果有人想为您的数据结构编写阅读器,他们可以导入您的头文件或以他们选择的语言实现数据结构 - 在大多数编程语言中,这应该非常简单。

    我建议你以这样的方式开始你的结构:

    typedef struct
    {
        int Version; // struct layout version
        int ByteSize; // byte size of structure for validation
        ...
    } MYDATA;
    

    这样,当您的数据结构被传递时,您的代码可以验证分配的结构大小是否与您期望的给定版本的结构的字节数相匹配。然后,您只需更新版本字段并检查新大小即可轻松引入结构的新版本。

    当您将数据保存到磁盘时,请确保将其逐个字段写入,而不是通过一次写入(使用指针和 sizeof() 以确保其他语言不必处理您的 C++ 编译器可能决定放入的潜在填充。可以在结构中手动布置字段以便没有填充,但您必须非常非常小心,这样做很容易出错。

    【讨论】: