• 一种向后兼容的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++的角度来看,上述思路还有许多改进的地方,在此仅做抛砖引玉,欢迎各位的讨论

  • 相关阅读:
    最长k可重区间集问题【网络流24题】
    分配问题 【网络流24题】
    航空路线问题 【网络流24题】
    最长不下降子序列问题 【网络流24题】
    数组去重
    项目选题报告(团队)
    关于DLL的创建与使用简单描述(C++、C#)
    原型设计(结对第一次)
    团队展示(团队)
    第二次作业——个人项目实战
  • 原文地址:https://www.cnblogs.com/webbery/p/10236152.html
Copyright © 2020-2023  润新知