c++ - 在代码中定义指令以分隔代码版本

标签 c++ c-preprocessor preprocessor-directive

我正在修改别人写的代码。

现有代码包含算法,适用于两种情况。差别很小——一些数据类型,有时在处理过程中有一些额外的行。

原始代码将所有内容设置为

#define ABC

#ifdef ABC
typedef struct {
    unsigned char a, b, c;
} MyStruct;
#else  
typedef unsigned char MyStruct;
#endif 

#if defined(ABC)
#define SIZE1 3
#else  
#define SIZE1 2
#endif 

typedef struct {
    MyStructtable[SIZE1];
} MyStructA;

void MyMethod(){
   statement1;
   #if defined(ABC)
   staement2;
   #endif
}

如果在源代码内部做出选择,它会起作用,但我试图为用户提供使用一个版本或另一个版本的选择,并且最难分离代码。

我可以为每个版本创建一套完整的文件。我遇到的问题:

由于有复杂的算法,将算法放在一个地方是有意义的......如果在一个版本而不是其他版本中进行更改,复制代码可能会导致错误。

我已经尝试过这种类型的事情

if(option)
{
  #define ABC
} 
else
{
  #undef ABC
}

但调试器会跳过并且即使在越过"is"分支时也看不到定义的 ABC。

有没有办法解开这段代码,或者我是否必须创建清晰的单独路径 - 复制整个代码?

最佳答案

我以前做过这样的事情。我有一个没有源代码的库,但我有一个标题。也有大量代码使用了这个库,远远超出了我自己可以更改的范围。

好吧,我不得不编写另一个库来完成与另一个库相同的事情,但使用不同的设备、驱动程序等。你明白了。所以在运行时我想用我的库替换他们的,但仍然能够使用旧的(必须向后兼容)。

所以我所做的是:我想出了一些方便的宏,可以将文本转换为其他定义。有 2 个头文件和 1 个源文件。例如,假设我想用 FooLibrary 替换运行时可选择的 BarLibrary。在一个名为 FooLibrary.h 的头文件中:

// don't ask me why, but two levels of indirection is necessary
// to pull off a certain concatenation
// this I got from another question on SO
#define CAT(x, y) x ## y
#define cat(x, y) CAT(x, y)

#ifdef FOO_HIJACK_NAMESPACE

// library init functions
bool ShouldHijack();
void SetHijack(bool yn);

// handy-dandy function-changing macros
#define FOO_FN_NAME(X) cat(X, FOO_HIJACK_NAMESPACE)
#define FOO_FN_NAME_NS(X) FOO_HIJACK_NAMESPACE :: FOO_FN_NAME(X)

#define FOO_DECL(X) FOO_API FOO_FN_NAME(X)
#define FOO_IMPL(X) FOO_API FOO_FN_NAME_NS(X)
#define FOO_CALL(X) (ShouldHijack() ? FOO_FN_NAME_NS(X) : X)

namespace FOO_HIJACK_NAMESPACE {
#else

#define FOO_DECL(X) X
#define FOO_IMPL(X) X
#define FOO_CALL(X) X

#endif // FOO_HIJACK_NAMESPACE

然后,在这个命名空间包装器中,您可以像这样重新定义函数调用:假设在您的其他库中有一个函数 bool BarFun(fooargs...),您可以将其重新定义为

bool FOO_DECL(BarFun)(fooargs...);
// more FOO_DECL's...

在 FooLibrary.h 中。 FOO_API 是您常用的 cdecl 类型的垃圾,例如,对我来说,我正在做 #define FOO_API WINAPI。添加类似的声明直到最后,不要忘记关闭命名空间:

#ifdef FOO_HIJACK_NAMESPACE
}
#endif // FOO_HIJACK_NAMESPACE

然后,在 FooLibrary.cpp 中,您提供如下实现:

#include "FooLibrary.h"

// hijack
static bool bShouldHijack = false;
bool ShouldHijack() { return bShouldHijack; }
void SetHijack(bool yn) { bShouldHijack = yn; }

bool FOO_IMPL(BarFun)(fooargs...)
{
    // your implementation here...
}
// more FOO_IMPL's...

等等。最后,你再制作一个我喜欢称之为“FooLibraryHijacked.h”的头文件:

#include "FooLibrary.h"
#define BarFun FOO_CALL(BarFun)
// more #define's...

为每个函数提供一个重新声明、一个重新实现和一个#define。我有大约 20 个这样的函数,所以我使用正则表达式为我生成了它们。

最后,要全部使用它,在目标代码文件中,#include "FooLibraryHijacked.h" 作为最后一个 #include,或者至少在您 #include "BarLibrary.h"。然后 #defineFOO_HIJACK_NAMESPACE 作为某种东西,说“Foo”,然后 BarFun 变成 Foo::BarFunFoo预处理器会掌握它。

现在如何使用单独的类型定义来做到这一点将很有趣。如果应用程序代码仅将变量从一个函数传递到另一个函数,或许您可以使用不透明指针。

关于c++ - 在代码中定义指令以分隔代码版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25371066/

相关文章:

objective-c - Xcode 中 #elseifdef 的预处理指令无效

c - 使用递增的变量在 c 中粘贴 token

c++ - Linux OpenSuse c++ 编译器问题。 (无法初始化节调试器的解压缩状态)

c - "#define PACK_OFFSET ((size_t)((pack_t *)0)->data)"是什么意思

计算总 sizeof 参数的 C 函数

c - 通配符风格的 C 预处理器技巧

c - 来自供应商的预处理指令使用括号而不是引号定义

c++ - STL 容器类 : array

C++:按引用返回到按值返回

c++ - 反转优先级队列中元素的顺序