如何在编译时显示#define的值?


123

我试图找出我的代码认为正在使用的Boost版本。我想做这样的事情:

#error BOOST_VERSION

但是预处理器不会扩展BOOST_VERSION。

我知道我可以在程序运行时将其打印出来,并且我可以查看预处理器的输出以找到答案。我觉得在编译过程中采取这种方式可能会很有用。


7
对于未来的访客...克里斯·巴里(Chris Barry)最后提供了通用的解决方案(没有Boost专用的东西)。
jww

Answers:


117

我知道这是原始查询之后的很长时间,但这可能仍然有用。

这可以在GCC中使用字符串化运算符“#”来完成,但是需要两个阶段。

#define XSTR(x) STR(x)
#define STR(x) #x

宏的值然后可以显示为:

#pragma message "The value of ABC: " XSTR(ABC)

请参阅:gcc在线文档中的3.4字符串化。

这个怎么运作:

预处理器理解带引号的字符串,并与普通文本不同地处理它们。字符串串联是这种特殊处理的一个示例。消息编译指示需要使用带引号的字符串作为参数。如果参数包含多个组件,则它们必须全部为字符串,以便可以应用字符串连接。预处理器永远不能假定未加引号的字符串应被视为已被加引号。如果这样做,则:

#define ABC 123
int n = ABC;

不会编译。

现在考虑:

#define ABC abc
#pragma message "The value of ABC is: " ABC

相当于

#pragma message "The value of ABC is: " abc

这将导致预处理器警告,因为abc(未加引号)无法与前面的字符串连接。

现在考虑预处理器字符串化(以前称为字符串化,文档中的链接已更改,以反映修订的术语。(顺便说一句,这两个术语都是可憎的。正确的术语当然是字符串化。准备更新)您的链接。))运算符。这仅对宏的参数起作用,并将未扩展的参数替换为双引号中的参数。从而:

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

将为s1和s2分配相同的值。如果运行gcc -E,则可以在输出中看到它。也许将STR更好地命名为ENQUOTE。

这解决了将引号引起来的问题,现在的问题是,如果参数是宏,则宏将不会扩展。这就是为什么需要第二个宏的原因。XSTR扩展其参数,然后调用STR以将扩展的值放入引号中。


3
我很好奇为什么需要两个阶段
Vincent Fourmond

4
@VincentFourmond如果没有XSTR阶段,则宏不会扩展。因此,如果您#define ABC 42 \ n STR(ABC),则会得到“ ABC”。参见gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c

这也适用于Xcode 8,例如用替换ABC __IPHONE_9_3
funroll

GCC术语似乎已更改,URL也随之更改,URL现在为https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
克里斯·巴里

119

BOOST_PP_STRINGIZE 对于C ++似乎是一个很好的解决方案,但对于常规C而言却不是。

这是我的GNU CPP解决方案:

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

上面的定义导致:

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

对于“定义为整数”“定义为字符串”“定义但没有值”变量,它们可以正常工作。仅对于“未定义”变量,它们显示的内容与原始变量名称完全相同。您必须习惯了-也许有人可以提供更好的解决方案。


优秀的!有ARM RVCT经验吗?似乎没有“字串”的特征为GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/...
xdan

2
很好的解决方案。但是,如果我希望显示编译时计算值的大小,例如复杂结构的大小,可以这样做吗?该答案中建议的方法似乎是在生成DEFINED_INT=(sizeof(MY_STRUCT)),而没有对sizeof运算符进行评估。
卡尔

(注释补充:这并不sizeof奇怪,因为它将进行评估的是编译器而不是预处理器,但是,仍然想知道是否有巧妙的方法来实现这一点。)
卡尔

@xdan好的解决方案,不幸的是并不能满足诸如#define masks {0xff, 0xaf, 0x0f}
Simon Bagley,

59

如果您使用的是Visual C ++,则可以使用#pragma message

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

编辑:感谢LB的链接

显然,GCC等效项(未经测试):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
这就是所谓的诊断性编译指示,gcc.gnu.org
onlinedocs / gcc /…

4
如果您包括其中的定义BOOST_PP_STRINGIZE好且简短且可复制/粘贴那将是很好的。
Timmmm '17

在gcc下工作正常:)
Thomas Legris

14

据我所知,'#error'仅会打印字符串,实际上您甚至不需要使用引号

您是否尝试过使用“ BOOST_VERSION”编写各种故意不正确的代码?也许像“ blah [BOOST_VERSION] = foo;”之类的东西。会告诉您“字符串文字1.2.1不能用作数组地址”之类的内容。这不会是一个错误的消息,但至少它将向您显示相关值。您可以反复尝试,直到找到确实告诉您该值的编译错误为止。


因为BOOST_VERSION是一个整数,所以那没有用,但是我在下面的语句中看到了它:std::vector<BOOST_VERSION>;在gcc 4.4.1中。谢谢!
Jim Hunziker,2009年

请注意,使用Visual C ++,您将不得不使用Bojan Resnik的答案。
拉斐尔·圣皮埃尔

我试图使它起作用,但是GCC给我的错误消息令人难以描述。但是+1是提及它。
克里斯·卢兹

14

没有提升:

  1. 再次定义相同的宏,编译器HIMSELF将发出警告。

  2. 从警告中可以看到先前定义的位置。

  3. 先前定义的vi文件。

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}

这是更容易和直接的。
Tmx

1
本身:编译器没有性别
天空

这不适用于预定义的宏,例如__cplusplus
ManuelAtWork

10

在Microsoft C / C ++中,您可以使用内置函数_CRT_STRINGIZE()来打印常量。我的许多stdafx.h文件都包含以下内容的组合:

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

并输出如下内容:

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000

5
#define a <::BOOST_VERSION>
#include a
MSVC2015:致命错误C1083:无法打开包含文件:':: 106200':没有此类文件或目录

即使preprocess to file已启用,即使存在无效令牌也可以使用:

#define a <::'*/`#>
#include a
MSVC2015:致命错误C1083:无法打开包含文件:'::'* /`#':没有这样的文件或目录
GCC4.x:警告:缺少终止'字符[-Winvalid-pp-token]#定义
<:: '* /`#>

我只是说Build error: #include expects "FILENAME" or <FILENAME>。叹。
endolith '18

@endolith什么编译器和版本?
安德里(Andry)'18年

DP8051 Keil 9.51 :)
Endolith '18

@endolith似乎该编译器在预处理上非常受限制:keil.com/support/man/docs/c51/c51_pp_directives.htm但是,在我看来,它几乎可以按预期工作,我只是删除了一些无效字符,例如'*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry

谢谢,这为我节省了时间,因为在我使用的编译器中未实现杂注消息。
CodeMonkey


2

你在找吗

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

如我所假设的,如果BOOST_VERSION是字符串,那不是很好,但是也可能为主,次和修订号定义了单个整数。


我认为提交者不想(只是)强制使用特定值,他们想查看当前值是什么。
KeyserSoze

这是唯一对我有用的东西。我可以随时更改#if VARIABLE == 123语句,语法高亮显示告诉我它是否是我认为的值……
endolith

2

查看预处理器的输出与您要求的答案最接近。

我知道您已经排除了该方法(以及其他方法),但是我不确定为什么。您有足够具体的问题要解决,但是您没有解释为什么任何“正常”方法都不适合您。


这可能是对一般问题的正确答案。
jww

1

您可以编写一个程序,将其打印出来BOOST_VERSION并进行编译并作为构建系统的一部分运行。否则,我认为您不走运。


对于在标头中定义的软件版本,您可能是安全的(这是一个很好的答案)。但是,作为一般解决方案,可能的弊端是使您的测试应用和真实应用具有相同的#define值-取决于它们的包含路径,其他#define可用于设置该值,在CFLAGS传递到编译器等
KeyserSoze

从您的真实程序中打印出来。如果是图形,则将其放在“关于”对话框中。如果是命令行,则将其设为一个选项(可能是--version的一部分)。如果是守护程序,请将其写入日志文件。如果是嵌入式的,请寻找其他方法。
divegeek

@swillden-OP在编译时而不是在运行时需要它。
克里斯·卢兹

这也往往会破坏基于交叉编译器的版本
Craig Ringer 2015年



0

在使用宏之前,请尝试重新定义宏,而不是#error。编译将失败,并且编译器将提供它认为适用于宏的当前值。

#define BOOST_VERSION等等

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.