Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在程序版本更新时处理更改的数据结构?_C++_C_Embedded - Fatal编程技术网

C++ 如何在程序版本更新时处理更改的数据结构?

C++ 如何在程序版本更新时处理更改的数据结构?,c++,c,embedded,C++,C,Embedded,我做嵌入式软件,但我想这不是一个真正的嵌入式问题。我不(不能因为技术的原因)使用像MySQL这样的数据库,只是C或C++结构。 对于如何处理这些结构在不同版本的程序中的布局变化,是否有一个通用的理念 让我们拿一本通讯录。从程序版本x到x+1,如果: 一个字段被删除(似乎足够简单)或添加(如果所有字段都可以使用一些新的默认值,则可以) 字符串变长还是变短?整数从有符号/无符号的8位变为16位 也许我会将姓氏/名字组合起来,或者将名字分成两个字段 这些只是一些简单的例子;我不是在寻找这些问题的答

我做嵌入式软件,但我想这不是一个真正的嵌入式问题。我不(不能因为技术的原因)使用像MySQL这样的数据库,只是C或C++结构。 对于如何处理这些结构在不同版本的程序中的布局变化,是否有一个通用的理念

让我们拿一本通讯录。从程序版本x到x+1,如果:

  • 一个字段被删除(似乎足够简单)或添加(如果所有字段都可以使用一些新的默认值,则可以)
  • 字符串变长还是变短?整数从有符号/无符号的8位变为16位
  • 也许我会将姓氏/名字组合起来,或者将名字分成两个字段
这些只是一些简单的例子;我不是在寻找这些问题的答案,而是在寻找一个通用的解决方案

显然,我需要一些硬编码的逻辑来处理每个更改

如果有人没有从版本x升级到x+1,而是等待x+2怎么办?我应该尝试合并这些更改,还是只应用x->x+1,然后再应用x+1->x+2

如果版本x+1有缺陷,我们需要回滚到以前版本的s/w,但已经“升级”了数据结构,该怎么办

我倾向于TLV(),但可以看到很多潜在的头痛问题


这并不是什么新鲜事,所以我只是想知道其他人是如何做到这一点的。

您可能想看看库是如何处理这个问题的。

人们使用的关系数据库有一个巨大的概念

这被称为将体系结构分为“逻辑”层和“物理”层

您的结构既是一个逻辑层,又是一个物理层,将它们混成一个难以更改的东西

您希望您的程序依赖于逻辑层。您希望逻辑层反过来映射到物理存储。这允许你在不破坏东西的情况下进行更改

您不需要重新发明SQL来实现这一点

如果您的数据完全保存在内存中,那么请考虑这一点。将物理文件表示形式与内存中表示形式分离。以某种“通用”、灵活、易于解析的格式(如JSON或YAML)编写数据。这允许您以通用格式读取,并在内存中构建高度特定于版本的结构

如果您的数据被同步到一个文件系统上,那么您还有更多的工作要做。再次,看看RDBMS的设计思想


不要为一个简单的无脑结构编写代码。创建一个“记录”,将字段名映射到字段值。这是一个名值对的链表。这很容易扩展,可以添加新字段或更改值的数据类型。

如果您谈论的是在C API中使用的结构,那么有一些简单的指导原则:

  • 在结构的开头有一个structure size字段-这样,使用结构的代码可以始终确保它们只处理有效数据(例如,Windows API使用的许多结构都是以cbCount字段开头的,因此这些API可以处理针对旧SDK甚至添加了字段的新SDK编译的代码进行的调用
  • 永远不要删除字段。如果您不再需要使用它,这是一件事,但是为了使处理使用旧版本结构的代码时保持正常,请不要删除该字段
  • 包含版本号字段可能是明智的,但通常可以使用计数字段来实现此目的
这里有一个例子——我有一个引导加载程序,它在程序映像中查找固定偏移量处的结构,查找有关该映像的信息,该映像可能已被闪存到设备中

加载器已被修改,并且它支持结构中的其他项以进行某些增强。但是,可能会刷新较旧的程序映像,并且该较旧的映像使用较旧的结构格式。由于从一开始就遵循了上述规则,因此较新的加载器完全能够处理此问题。这是最简单的部分

如果结构被进一步修改,并且一个新的图像在一个带有旧加载程序的设备上使用新的结构格式,那么加载程序也可以处理它——它只是不会对增强功能做任何事情。但是,由于没有字段已经(或将要)删除后,旧的加载程序将能够执行其设计的任何操作,并使用具有新信息的配置结构的新映像执行


如果您所说的是一个实际的数据库,其中包含有关字段等的元数据,那么这些指导原则实际上并不适用。

您所寻找的是向前兼容的数据结构。有几种方法可以做到这一点。下面是低层方法

struct address_book
{
  unsigned int length; // total length of this struct in bytes
  char items[0];
}
其中,“items”是描述自身大小和类型的结构的可变长度数组

struct item
{
  unsigned int size; // how long data[] is
  unsigned int id;   // first name, phone number, picture, ...
  unsigned int type; // string, integer, jpeg, ...
  char data[0];
}
在您的代码中,您可以通过一些智能转换来迭代这些项目(地址簿->长度将告诉您何时到达终点)。如果您击中了一个您不知道其ID或不知道如何处理其类型的项目,您只需跳过该数据即可(从项目->大小)然后继续下一个。这样,如果有人在下一个版本中发明了一个新的数据字段或删除了一个数据字段,则您的代码能够处理它。您的代码应该能够处理有意义的转换(如果员工ID从整数变为字符串,则可能应该将其作为字符串处理)但是,我发现,这些情况很少见,通常可以用普通代码来处理。

最近我使用数据。这是BitTorrent使用的格式。简单,你可以很容易地检查它,所以比二进制数据更容易调试,而且被压缩。我从高质量C++中借用了一些代码。例如,在读回字段时检查字段是否存在。对于gzip压缩文件,只需执行以下操作:

ogzstream os(meta_path_new.c_str(), ios_base::out | ios_base::trunc);
Bencode map(Bencode::TYPE_MAP);
map.insert_key("url", url.get());
map.insert_key("http", http_code);
os << map;
os.close();

过去我使用过Sun的XDR格式,但现在我更喜欢这种格式。此外,使用其他语言(如perl、python等)阅读更容易。

在结构中嵌入版本号,或者像Win32那样执行,然后
igzstream is(metaf, ios_base::in | ios_base::binary);
is.exceptions(ios::eofbit | ios::failbit | ios::badbit);
try {
   torrent::Bencode b;
   is >> b;
   if( b.has_key("url") )
      d->url = b["url"].as_string();
} catch(...) {
}