【问题标题】:Setting/understanding how #define is used in a header file (setting from cmakelist)设置/理解如何在头文件中使用#define(来自 cmakelist 的设置)
【发布时间】:2023-04-11 00:33:01
【问题描述】:

我正在编写一个简单的日志库,并尝试在日志级别上设置编译时间定义。如果我将 #define LOG_COMPILE_MIN_LEVEL 放在头文件中,则不会在编译时设置它,但是如果 #define LOG_COMPILE_MIN_LEVEL 在 c 文件中,它确实可以工作。

这里是相关部分的sn-ps

CMakeLists.txt

// Trying to set LOG_COMPILE_MIN_LEVEL to ERROR
add_definitions(-DLOG_COMPILE_MIN_LEVEL=ERROR)
# I have also tried
add_compile_definitions(LOG_COMPILE_MIN_LEVEL=ERROR)

log.h


// I define in this file the following (technically they are enums with values set)
// DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3

// By default resort to INFO if not found
#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif

// In log.h I am trying to do this, where before even going to
// the log function (log_log) the level is checked against the compile level
// So since I (in theory) set LOG_COMPILE_MIN_LEVEL to ERROR, any INFO levels
// are not logged since INFO (value of 1) is below LOG_COMPILE_MIN_LEVEL which is 
// set to ERROR (value of 3)
// Ideally/in theory the compiler will compile out any messages not used by doing this....
#define LOG(level, __FILENAME__, __LINE__, ...) \
  if (level < LOG_COMPILE_MIN_LEVEL);\
  else log_log(level, __FILENAME__, __LINE__, __VA_ARGS__)

// The reason for having LOG and LOG_COMPILE_MIN_LEVEL in the header is cause I provide
// some logging macros like:
#define LOG_ERROR(...) LOG(ERROR, __FILENAME__, __LINE__, __VA_ARGS__)

// In some c/cpp file, this should not work since log level is set to
// ERROR but it does (because the default value of LOG_COMPILE_MIN_LEVEL is
// info.
LOG_INFO("My info message);

无论我在 CMakeLists.txt 中尝试过什么,我都无法让它工作。现在当我移动时

#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif

对于 c/cpp 文件,它确实有效(设置为 ERROR)。

  1. 为什么?
  2. 发生了什么?
  3. 头文件如何设置?

我试图在头文件中设置/定义它的原因是#define LOG 方法。

【问题讨论】:

  • 请发布完整的minimal reproducible exampleLOG_INFO 未定义。
  • 您不能为头文件指定传递给编译器的编译定义(至少不能通过常规编译),因为您传递 C/CPP 源文件,因此每次您都需要使用这些编译定义编译一个包含log.h 的文件,即使它是间接编译的。不确定您的情况出了什么问题,因为既没有显示行为的源文件,也没有用于创建问题中显示的构建系统的CMakeLists.txt 文件。

标签: c++ c cmake


【解决方案1】:

可能出了什么问题

虽然该示例不适合复制,并且没有提供INFO 的定义,但基于token 定义为enum 的解释,我有很强的补充说明这就是问题所在。 enum 类型不能很好地与宏混合,因为 enum 是在编译时构造的,而宏是在预处理时(编译开始之前)评估的。

工作示例

该方法应该适用于 .c 和 .h 文件。我做了一个最小的例子,效果很好:

test.h

#define DEBUG = 0
#define INFO = 1
#define WARN = 2
#define ERROR = 3

#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif

test.c

#include "test.h"

int main(int argc, char *argv) {
    printf("%i\n",LOG_COMPILE_MIN_LEVEL);
#if LOG_COMPILE_MIN_LEVEL == 1
    printf("Log set to INFO\n");`
#endif
}

将文件仅通过预处理器阶段gcc -E test.c 后,我得到以下输出:

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1
# 2 "test.c" 2

int main(int argc, char *argv) {
 printf("%i\n",1);

 printf("Log set to INFO\n");`

}

如您所见,预处理器已识别宏并正确执行了替换。

疑难解答

C/C++ 编译器只允许您生成预处理文件。预处理文件的作用是帮助调试此类问题,因此创建一个最小示例(如我的案例)并仅通过预处理器传递它。对于 GCC,-E 是告诉编译器生成并在stdout 上呈现文件的预处理版本的指令。

【讨论】:

    猜你喜欢
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-02
    • 1970-01-01
    • 2011-09-06
    • 2011-08-16
    • 1970-01-01
    相关资源
    最近更新 更多