C++ 如何在程序版本更新时处理更改的数据结构?
我做嵌入式软件,但我想这不是一个真正的嵌入式问题。我不(不能因为技术的原因)使用像MySQL这样的数据库,只是C或C++结构。 对于如何处理这些结构在不同版本的程序中的布局变化,是否有一个通用的理念 让我们拿一本通讯录。从程序版本x到x+1,如果:C++ 如何在程序版本更新时处理更改的数据结构?,c++,c,embedded,C++,C,Embedded,我做嵌入式软件,但我想这不是一个真正的嵌入式问题。我不(不能因为技术的原因)使用像MySQL这样的数据库,只是C或C++结构。 对于如何处理这些结构在不同版本的程序中的布局变化,是否有一个通用的理念 让我们拿一本通讯录。从程序版本x到x+1,如果: 一个字段被删除(似乎足够简单)或添加(如果所有字段都可以使用一些新的默认值,则可以) 字符串变长还是变短?整数从有符号/无符号的8位变为16位 也许我会将姓氏/名字组合起来,或者将名字分成两个字段 这些只是一些简单的例子;我不是在寻找这些问题的答
- 一个字段被删除(似乎足够简单)或添加(如果所有字段都可以使用一些新的默认值,则可以)
- 字符串变长还是变短?整数从有符号/无符号的8位变为16位李>
- 也许我会将姓氏/名字组合起来,或者将名字分成两个字段
这并不是什么新鲜事,所以我只是想知道其他人是如何做到这一点的。您可能想看看库是如何处理这个问题的。人们使用的关系数据库有一个巨大的概念 这被称为将体系结构分为“逻辑”层和“物理”层 您的结构既是一个逻辑层,又是一个物理层,将它们混成一个难以更改的东西 您希望您的程序依赖于逻辑层。您希望逻辑层反过来映射到物理存储。这允许你在不破坏东西的情况下进行更改 您不需要重新发明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(...) {
}