如何包含同一头文件的两个不同版本?

4

我正在编写文件转换代码,将专有文件格式转换为更通用的格式。我的目标是支持制造商文件格式的多个版本。

我有多个相同专有头文件的版本。这些头文件定义了构成主文件头的各种结构体(文件只是一个由原始数据跟随的大型头文件)。

我需要读取源文件的前4个字节以确定文件版本。文件版本反过来告诉我使用哪个版本的C-structs来创建文件。

问题是:

  1. 我不能修改专有头文件
  2. 头文件不使用命名空间或类
  3. 头文件中定义了一些宏

可能的解决方案:

  • 为每个文件版本类型构建不同的转换器二进制文件:-(
    • 对用户和开发人员都不方便
  • 动态加载每个版本的库
    • 转换器是插件导向的,因此这已经经常发生

我尝试过使用命名空间进行修改:

namespace version1 {
    #include "version1.h"
}

namespace version2 {
    #include "version2.h"
}

int main (void) {
    version1::header *hdr = new version1::header;
    return 0;
}

但是由于有预编译指令,而且每个头文件中都重新定义了多个宏,因此这种方法行不通。
有没有一种优雅的方法来处理这个问题?

这些头文件有多“C语言”?你只使用头文件,没有库文件或者支持它们的.c.cpp文件吗?你说你不能修改这些头文件:那么你能否将这些头文件作为输入,生成派生代码进行编译? - Yakk - Adam Nevraumont
它们是纯C头文件。甚至没有任何函数声明,只有C结构体。 - joe
@MatsPetersson 可能有一个合理的方法。 - Yakk - Adam Nevraumont
1个回答

5

您可以使用两个不同的源文件,以及前向声明:

// Forward declare in main.cpp:

namespace version1
{
   struct header;
}

namespace version2
{
   struct header;
}

// version1.cpp:

namespace version1
{
      #include <version1.h>
}

version1::header* new_v1_header()
{
   return new version1::header;
}

// other functions using `version1::header`

// version2.cpp:

namespace version2
{
      #include <version2.h>
}

version2::header* new_v2_header()
{
   return new version2::header;
}

// other functions using `version2::header`

另一个选择是实现一个包装类,它具有一个基类,该基类只是一个空壳:

class header_base
{
     virtual int func1(char *stuff) = 0; 
     ... many other virtual functions. 
};

// Create implementation of header_v1 or header_v2:
header_base* make_header(unsigned int magic);

header_base.cpp:

#include "header_v1.h"
#include "header_v2.h"

header_base* make_header(unsigned int magic)
{
    switch(magic)
    {
       case Magic_V1: 
          return new header_v1;
       case Magic_V2: 
          return new header_v2;
       default:
          assert(0);
          return 0;
    }
}

然后,在两个不同的地方实现:

在 headerv1.h 中:

class header_v1 : public header_base
{
    int func1(char *stuff);
    ... 
};

header_v1.cpp:

#include "header1.h"

int header_v1::func1(char *stuff)
{
   ... 
   return 17;
}

同样适用于header_v2.h和header_v2.cpp。

1
如果库不是头文件库,这并不是一个特别好的主意,因为它会干扰编译器生成的符号名。请参阅https://dev59.com/JGw15IYBdhLWcg3wWqZ0#6670753。 - R. Martinho Fernandes
我会做的正是这样。我还可能声明一个ABC来包装头文件声明的功能,并且有两个具体的派生类;每个版本一个。 - John Dibling
结构体只是定义了数百种基本类型,没有函数。为了动态使用header_base,我必须存储指向顶层结构体的指针,然后为每个结构体成员创建虚拟getter,然后为每个派生包装类实现每个成员的getter。 - joe
这听起来像是正确的做法是将差异化下降一到三个级别。或者,只需更改实际的头文件... - Mats Petersson
最终我会采用你提出的第二个方案...我创建了一个虚拟类作为头文件的接口,我将存储(void*)引用到顶层结构体,以便将所有内容编译成一个二进制文件,或者回退到动态加载包含实现类的库。 - joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接