一种向后兼容的C++结构体设计

问题产生的背景:
有时候,我们需要维护老旧代码。这些代码经常因为需求变更而变化。最常见的升级就是接口的升级,诸如增加新的函数接口、扩展函数的参数、扩展协议等等。在此我们讨论一种较为少见的情形,即存储于设备中的一段二进制结构的升级。这种情况类似于网络通讯中的序列化,但又有所不同。关于如何设计序列化结构的文章有许多,我们在此不做讨论。
设计目标:
1. 为了兼容老版本的结构体
2. 为了支持内存拷贝初始化
3. 版本号的支持
4. 尽量少的代码修改

假设我们第一次(旧)的数据结构如下:

struct Old{
  int i;
};

首先,我们期望能对后续升级的结构体带有版本号。最简单的想法是在结构体中添加一个int类型的版本信息。但是,当我们深入考虑时,首先想到的一个问题就是,我们该如何从一段内存区中得到这个版本信息。如果我们添加了版本字段,那么我们首先需要找到这个字段,得到其版本号,然后再把这个缓冲区的数据转换成对应版本的数据结构。显然,我们是知道这个字段所在的内存偏移量的。于是我们的实现代码大概如下:

struct V1{
  int i;
  int version; //version==1
};
struct V2{
  int i;
  int version; //version==2
  int j;
};
//
unsigned char* buff=new unsigned char[100];
int len = 0;
getStruct(buff,&len);
int* pVersion = &(buff[4]);

于是我们拿到了结构体的版本号,可以根据版本号得到具体的数据类型了。然而仔细考察一下可以发现,实际上我们并不需要这个版本号,因为每一次升级,数据结构都是在原有的基础上添加的,因此这个结构体的长度会随着版本号的增加而增加,所以我们可以利用这个结构体的长度(注意对其可能导致长度相同的问题),来作为区分版本的关键。于是,我们省去了一个int的长度。

为了能够区分版本,我们在上面的结构体名字当中使用了诸如1、2之类的标志。实际上,我们可以利用C++语法的模板来代替这些常量,以确保代码的易读性。于是结构体的定义更改为:

template<int VERSION> struct V{};
template<> struct V<0>{
  int i;
}
template<> struct V<1>{
  int i;
  int j;
};

为了保证能够与C的结构体兼容,我们还需要保证我们的结构体是POD类型。因此我们不能在结构体中定义任何初始化函数,也不能使用继承。为了保证这一规范,我们采用静态断言,提前为未来的升级做约束:

static_assert(std::is_pod<V<0> >::value==true,"V<0> is not a POD type");
static_assert(std::is_pod<V<1> >::value==true,"V<1> is not a POD type");
static_assert(std::is_pod<V<2> >::value==true,"V<2> is not a POD type");
static_assert(std::is_pod<V<3> >::value==true,"V<3> is not a POD type");

于是我们可以这样去使用这个结构体:

getStruct(buff,&len);
switch(len){
  case sizeof(V<0>): {
    V<0>* pV=(V<0>*)buff;
  }
  break;
  case sizeof(V<1>): {
    V<1>* pV=(V<1>*)buff;
  }
  break;}

从C++的角度来看,上述思路还有许多改进的地方,在此仅做抛砖引玉,欢迎各位的讨论

原文地址:https://www.cnblogs.com/webbery/p/10236152.html

时间: 2024-10-13 22:03:37

一种向后兼容的C++结构体设计的相关文章

golang 兼容不同json结构体解析实践

线上服务器,同一个web接口有时需要兼容不同版本的结构体.这种情况思路是使用interface{}接收任意类型数据,结合reflect包处理. 如下,http接口调用者会传入不同的json结构数据(单体结构或切片结构): type ReqStu struct { XXX struct { //XXX结构为单体 AAA string `json:"aaa"` BBB string `json:"bbb"` CCC string `json:"ccc"

C语言-结构体定义的几种方式

若struct后面接的是名字,则其为该结构体的名称.第一种是最基本的结构体定义,其定义了一个结构体A. struct A //第一种 { int a; }; 第二种则是在定义了一个结构体B的同时定义了一个结构体B的变量m. struct B //第二种 { int b; }m; 第三种结构体定义没有给出该结构体的名称,但是定义了一个该结构体的变量n,也就是说,若是想要在别处定义该结构体的变量是不行的,只有变量n这种在定义结构体的同时定义变量才行. struct //第三种 { int c; }n

程序设计基石与实践系列之失落的C语言结构体封装艺术

英文来源于 Eric S. Raymond-- The Lost Art of C Structure Packing 谁该阅读这篇文章 本文是关于削减C语言程序内存占用空间的一项技术--为了减小内存大小而手工重新封装C结构体声明.你需要C语言的基本知识来读懂本文. 如果你要为内存有限制的嵌入式系统.或者操作系统内核写代码,那么你需要懂这项技术.如果你在处理极大的应用程序数据集,以至于你的程序常常达到内存的界限时,这项技术是有帮助的.在任何你真的真的需要关注将高速缓存行未命中降到最低的应用程序里

(转)失落的C语言结构体封装艺术

目录1. 谁该阅读这篇文章 2. 我为什么写这篇文章 3.对齐要求 4.填充 5.结构体对齐及填充 6.结构体重排序 7.难以处理的标量的情况 8.可读性和缓存局部性 9.其他封装的技术 10.工具 11.证明及例外 12.版本履历 1. 谁该阅读这篇文章 本文是关于削减C语言程序内存占用空间的一项技术——为了减小内存大小而手工重新封装C结构体声明.你需要C语言的基本知识来读懂本文. 如果你要为内存有限制的嵌入式系统.或者操作系统内核写代码,那么你需要懂这项技术.如果你在处理极大的应用程序数据集

C++中结构体和类的区别

在C++中,结构体是一种特殊形态的类. 结构体和类的唯一区别就是:  结构体和类具有不同的默认访问控制属性. 类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型(private) 结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型(public) C++中,不使用结构体丝毫不会影响程序的表达能力.C++之所以要引入结构体,是为了保持和C程序的兼容性. 但有时仍会在C++中使用结构体,是因为,可以使用结构体将不同类型数据组成整体,方便于保存数据.(若用类来保存,因类中成

结构体【struct】

一.结构体定义 概念:结构体是由一系列不同或相同基本类型数据组合而成的新的复合数据集合,从而使这些数据项组合起来反应一个信息. 意义:结构体的使用为处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且,它们为函数间传递不同类型的数据提供了方便. 特点: 1.结构体类型是用户自行构造的: 2.它由若干不同的基本数据类型的数据构成. 3.它属于C语言的一种数据类型,与整型.实型相当.因此,定义它时不分配空间,只有用它定义变量时才分配空间. 4.结构体类型中的成员名可以与程序中的变量名相同,两

黑马程序员-----结构体数组

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ----- 第一讲  结构体数组 一.结构体数组的概念       数组的元素也可以是结构类型的.因此可以构成结构型数组.结构数组的每一个元素都是具有相同结构类型的下表结构变量.在实际应用中,经常用结构数组来表示具有相同数据结构的一个群体.如一个班的学生

9、C语言——结构体

结构体 构造-定义-使用 构造类型--结构体类型 类型是用来定义变量的 结构体类型 使用结构体变量.结构体数组.结构体指针变量 一.结构体类型 1.构造结构体类型 struct 结构体类型名--遵循用户的标识符 { 成员1的定义 成员2的定义 ...... 成员n的定义 }; eg: struct student { int sn; int age; char sex; int s[3]; }:此处的分号不能省略· struct 是构造结构体类型的标志 其中的student不是变量名,是构造的体

C#学习笔记之结构体

1.概述 结构是一种与类相似的数据类型,不过它较类更为轻量,一般适用于表示类似Point.Rectangle.Color的对象.基本上结构能办到的类全都能办到,但在某些情况下使用结构更为合适,后面会有提到. 结构具有以下特点: 结构可以实现接口. 结构可以声明带参数的构造函数. 结构不能声明默认构造函数(没有参数的构造函数)或析构函数. 结构是值类型,而类是引用类型. 实例化结构体时可以不使用new运算符. 结构类型是不可抽象.隐式密封的,故不能使用abstract和sealed修饰符. 在结构