如何重命名C预处理宏?

15

考虑一个(只读第三方)头文件lib.h,其中包含:

#define XYZ 42

在源代码文件中,我想要使用单词XYZ表示一个无关的意思,并且不希望将其替换为42。但是,在同一源代码文件中,出于其他目的,我确实希望从lib.h中访问值42而不是硬编码它。如何将宏从XYZ重命名为LIB_XYZ

以下内容不起作用,因为预处理器希望在进行LIB_XYZ替换时存在XYZ,但XYZ已被未定义:

#include "lib.h"
#define LIB_XYZ XYZ
#undef XYZ

有没有一种方法可以欺骗预处理器,在 XYZ 丢失之前将 LIB_XYZ 展开为其最终值?


2
我认为你陷入了困境,如果你想从第三方头文件中获取LIB_XYZ的定义,而且不想使用除C编译器以外的任何东西。如果我必须做到这一点,我可能会编写一个脚本将lib.h转换为our_lib.h,并修改所选的#define,使我的代码包含our_lib.h,并使用makefile使正确的事情发生,如果lib.h改变了。 - Arlie Stephens
1
也许这个链接能够帮到你。 - Samir Aguiar
4个回答

8
至少在我所知道的情况下,不能使用预处理器进行翻译。
但是,对于像您示例中已知类型的简单常量,有一个解决方法。
#include <stdio.h>

// <xyz.h>

#define XYZ 42

// </xyz.h>

enum xyz_constants
{
  LIB_XYZ = XYZ,
};

#undef XYZ

#define XYZ 27

int
main()
{
  printf("old value: %d, new value: %d\n", LIB_XYZ, XYZ);
  return 0;
}

不考虑 stdio.h 中的内容,这段代码经过预处理后变成了以下形式。
enum xyz_constants
{
  LIB_XYZ = 42,
};

int
main()
{
  printf("old value: %d, new value: %d\n", LIB_XYZ, 27);
  return 0;
}

你可以在一定程度上将其扩展到其他数据类型和某些类似函数的宏,但当然有限制。
无论如何,为什么你需要特定的标识符XYZ?你不能为你的宏使用不同的名称吗?

1
枚举技巧真是太棒了!谢谢!关于为什么:XYZ是我通过连接构建的另一个第三方宏的子字符串,连接宏是通用的(适用于XYZ或ABC或任何其他内容),因此必须使用XYZ。 - alexei
这个解决方案真是救了我一命 :) - MByD
通常情况下,涉及到 #undef XYZ 的解决方案可能不起作用。库可能会在其宏的替换文本中引用 XYZ;如果未定义或重新定义为其他内容,则这些宏将会出错。如果您知道这些宏是什么并且可以避免使用它们,那么这个方法是可行的。 - Kaz

4
如果来自lib.hXYZ是一个数字[或常量变量],你可以使用enum:
enum { LIB_XYZ = XYZ };
#undef XYZ

如果XYZ不是上述情况,那么你需要创建(例如)myxyz.c文件,并在其中使用XYZ(其他文件可能包含xyz.h),但是不要引用lib.h。与此不同的是,#define LIB_XYZ XYZ将不会在该行被解析,只有在你后面使用它时才会被解析,例如:
foo(LIB_XYZ);

那样行不通,因为你已经使用#undef取消了XYZ


0
预处理器符号是一个名称。没有预处理指令可以更改名称本身,同时保留内容。例如,给定以下任一内容:
#define FOO 42

或者

#define FOO(x, y)  x ## y (

没有办法定义一个名为BAR的宏,其内容相同,而不重复这些定义。也就是说,没有像下面这样的操作:

#alias BAR FOO  // nonexistent fantasy macro-cloning preprocessor directive

不要像这样:
#rename BAR FOO // like #alias BAR FOO followed by #undef FOO

如果我们这样做:
#define BAR FOO  // for the #define FOO 42 case

这不是别名。宏BAR的定义使其替换令牌序列为令牌FOO,而不是42。如果FOO宏消失,则BAR将失去其含义。

还要注意,C预处理器宏不能扩展为预处理指令,因此以下方法也不起作用

 // wrong:
 #define MACRO_DEFINER(NAME) \
   #define NAME 42

 MACRO_DEFINER(FOO) // hoping for #define FOO 42: no such luck
 MACRO_DEFINER(BAR) // hoping for #define BAR 42: likewise

恐怕你需要退回几步,找到另一种解决问题的策略。如果卡住了,可以创建一个关于实际问题的新问题。

总是有代码生成:在构建时生成 C 或 C++ 代码。然后,如果您只调整生成系统,您可以梦想中的任何文本替换或扩展都是可能的。

任何涉及 #undef XYZ 的解决方案都会破坏库头文件的功能。库可能会做这样的事情:

#define XYZ 42 #define macro(B) bloop(XYZ, B)

如果我们使用 macro,那么如果重新定义 XYZ,我们就会破坏它。

如果该库已知定义了 XYZ,并且我们成功地在使用该库的代码中重新定义了它,则对未来的维护者来说,情况将会令人困惑。"哦,这个 XYZ 实际上不是那个库里的;这个程序员只是想要一个无关的 XYZ。"

最好的解决方案是停止想要使用 XYZ 并找到其他名称。适应您正在使用的库;不要与它们冲突。


0
使用另一个 .c 文件,并将宏值分配给全局变量。

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