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

Posted

技术标签:

【中文标题】如何在编译时显示 #define 的值?【英文标题】:How do I show the value of a #define at compile-time? 【发布时间】:2010-12-06 10:23:05 【问题描述】:

我试图弄清楚我的代码认为它正在使用哪个版本的 Boost。我想做这样的事情:

#error BOOST_VERSION

但预处理器不会扩展 BOOST_VERSION。

我知道我可以在程序运行时将其打印出来,并且我知道我可以查看预处理器的输出来找到答案。我觉得在编译期间有一种方法会很有用。

【问题讨论】:

对于未来的访问者... Chris Barry 最后提供了通用解决方案(没有 Boost 特定的东西)。 对于 Visual C++ 用户,还可以查看 Bojan Resnik 的回答,了解下面的 #pragma message 语法 【参考方案1】:

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

【讨论】:

对于在标题中定义的软件版本的情况,您可能是安全的(这是一个很好的答案)。但作为一般解决方案,可能的缺点是让您的测试应用程序和您的真实应用程序具有相同的#define 值 - 取决于它们的包含路径,其他可用于设置该值的#defines , CFLAGS 传递给编译器等 从您的真实程序中打印出来。如果是图形的,把它放在“关于”对话框中。如果是命令行,请将其设为选项(可能是 --version 的一部分)。如果是守护进程,请将其写入日志文件。如果嵌入,请寻找其他方式。 @swillden - OP 在编译时想要它,而不是在运行时。 这也会破坏基于交叉编译器的构建 这就是我最终要做的事情,即使我正在为 LINUX 的 AVR CPU 进行交叉编译。所有的预处理器计算都在头文件中,编写一个简短的程序来测试这些值很容易。我也可以使用公认的解决方案,但两者都比尝试读取对象转储文件更好。【参考方案2】:

据我所知,“#error”只会打印字符串,实际上是you don't even need to use quotes。

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

【讨论】:

那行不通,因为 BOOST_VERSION 是一个整数,但我用这个语句看到了它:std::vector<BOOST_VERSION>; in gcc 4.4.1。谢谢! 请注意,使用 Visual C++,您必须使用 Bojan Resnik 的答案。 我试图让它工作,但 GCC 给我的错误消息令人遗憾地无法描述。但是 +1 提到它。【参考方案3】:

你在找吗

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

如果 BOOST_VERSION 是一个字符串,这不是很好,就像我假设的那样,但也可能为主要、次要和修订号定义了单独的整数。

【讨论】:

我认为提交者不想(只是)强制执行特定值,他们想看看当前值是什么。 这是唯一对我有用的东西。我可以即时更改#if VARIABLE == 123 语句,语法突出显示告诉我它是否是我认为的值...【参考方案4】:

您还可以预处理源文件并查看预处理器值的结果。

【讨论】:

【参考方案5】:

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

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

【讨论】:

这可能是一般问题的正确答案。【参考方案6】:

BOOST_VERSION 在 boost 头文件 version.hpp 中定义。

【讨论】:

【参考方案7】:

如果你使用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)

【讨论】:

这就是所谓的诊断编译指示,gcc.gnu.org/onlinedocs/gcc/… 如果你包含the definition of BOOST_PP_STRINGIZE 会很好,它很好,很短并且可以复制/粘贴。 在 gcc 下工作正常 :)【参考方案8】:

还请查看 Boost 文档,了解如何使用宏:

参考BOOST_VERSION,来自http://www.boost.org/doc/libs/1_37_0/libs/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.boost_helper_macros:

在中描述 boost 版本号 XXYYZZ 格式,例如: (BOOST_VERSION % 100) 是次未成年人 版本,((BOOST_VERSION / 100) % 1000) 是次要版本,并且 (BOOST_VERSION / 100000) 是专业的 版本。

【讨论】:

【参考方案9】:

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/… 似乎没有“字符串化”功能 很好的解决方案。但是,如果我希望显示编译时计算值的大小,例如复杂结构的大小,可以这样做吗?此答案中建议的方法似乎生成DEFINED_INT=(sizeof(MY_STRUCT)),而没有评估sizeof 运算符。 (评论补充:并不意外,因为它是编译器而不是预处理器将评估sizeof,但是,如果有一个聪明的方法来实现这一点,仍然很好奇。) @xdan 很好的解决方案,不幸的是不适合#define masks 0xff, 0xaf, 0x0f【参考方案10】:

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

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

#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 将扩展后的值放入引号中。

【讨论】:

我很好奇为什么它需要两个阶段 @VincentFourmond 没有 XSTR 阶段,宏不会展开。所以如果你做了#define ABC 42 \n STR(ABC) 你会得到“ABC”。见gcc.gnu.org/onlinedocs/cpp/Stringification.html 这也适用于 Xcode 8,例如用__IPHONE_9_3替换ABC。 GCC 术语似乎发生了变化,URL 也随之改变,现在是 https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing 对我不起作用,我看到的消息是“ABC 的值:XSTR(ABC)”【参考方案11】:
#define a <::BOOST_VERSION>
#include a
MSVC2015:致命错误 C1083:无法打开包含文件:'::106200':没有这样的文件或目录

优点

适用于内置宏 即使启用了preprocess to file,即使存在无效令牌也可以工作:
#define a <::'*/`#>
#include a
MSVC2015:致命错误 C1083:无法打开包含文件:'::'*/`#':没有这样的文件或目录GCC4.x:警告:缺少终止 ' 字符 [-Winvalid-pp-token] #定义一个 <::>

缺点

有时会因为包含文件路径中的无效字符而失败。可以通过更改前缀来修复(请参阅下面的更新部分)。

更新

对于 GCC 4.7.x 及更低版本,输出会抛出错误:

错误:#include 需要“文件名”或

要解决这个问题,您可以更改前缀:

#define a <.__cplusplus>
#include a
fatal error: .201103L: No such file or directory

【讨论】:

我的只是说Build error: #include expects "FILENAME" or &lt;FILENAME&gt;。叹息。 @endolith 什么编译器和版本? DP8051 Keil 9.51 :) @endolith 似乎这个编译器在预处理方面非常有限:keil.com/support/man/docs/c51/c51_pp_directives.htm 但是,就我而言,它几乎可以按预期工作,我刚刚删除了一些无效字符,例如'*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`' 谢谢,这救了我,因为我使用的编译器中没有实现编译指示消息。【参考方案12】:

没有提升:

    再次定义相同的宏,编译器 HIMSELF 会给出警告。

    从警告中可以看到之前定义的位置。

    之前定义的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 ()



【讨论】:

这个更简单直接。 本身:编译器没有性别 这不适用于预定义的宏,例如__cplusplus【参考方案13】:

在 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

【讨论】:

【参考方案14】:

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

#define BOOST_VERSION 等等

【讨论】:

以上是关于如何在编译时显示 #define 的值?的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 在编译器开始编译时显示错误

运行给定程序时显示编译错误

mac编译java时生成class文件但运行时显示找不到或无法加载主类

请问怎么解决servlet编译时显示不存在mybean.date?

为啥 MyEclipse 在编译时显示 join_table not found 错误

使用 npm start 时显示编译失败无法解决