【问题标题】:Can `#ifdef` be used inside a macro?`#ifdef` 可以在宏中使用吗?
【发布时间】:2022-06-29 01:18:27
【问题描述】:

我只找到了this related question,这不是我想要的。

我曾经在 #ifdef 语句中定义宏:

#ifdef DEBUG
#   define PRINT_IF_DEBUGGING(format) printf(format);
#   define PRINTF_IF_DEBUGGING(format, ...) printf(format, __VA_ARGS__);
#else
#   define PRINT_IF_DEBUGGING(...)
#   define PRINTF_IF_DEBUGGING(...)
#endif

现在,我想做相反的事情,在宏中包含 #ifdef 语句。像这样的:

#define PRINT_IF_DEBUGGING(format, ...) \
#if defined(DEBUG) print(format); #endif
#define PRINTF_IF_DEBUGGING(format, ...) \
#if defined(DEBUG) printf(format, __VA_ARGS__); #endif

但是,我在 #ifdef defined 中使用 __VA_ARGS__ 时遇到问题。

error: '#' is not followed by a macro parameter
 #define PRINT_IF_DEBUGGING(format, ...)
error: '#' is not followed by a macro parameter
 #define PRINTF_IF_DEBUGGING(format, ...)
warning: __VA_ARGS__ can only appear in the expansion of a C++11 variadic macro
 #if defined(DEBUG) printf(format, __VA_ARGS__); #endif

这可能吗?

【问题讨论】:

  • 我越看越想为什么你想放弃一些可读和有效的东西。
  • 假设我正在探索使用宏的新方法。如果我的意图是可能的,我将能够使用#define DEBUG 为一段代码而不是整个程序代码启用printf
  • 也就是说,我希望编译器定义的代码不是在解析宏PRINTF_IF_DEBUGGING时定义的,而是在解析时使用它。

标签: c++ c++11 macros variadic-macros


【解决方案1】:

这确实应该是一条评论,但我无法以一种允许我说出我想说的话的方式来格式化它,所以我改为回答。

不管怎样,只要改变这个:

#if defined(DEBUG) print(format); #endif

到这里:

#if defined(DEBUG)
    print(format);
#endif

等等,应该可以解决它。

【讨论】:

  • Fwiw,我认为这应该是一个答案:-)
  • @TedLyngmo 引用Kenneth Wolstenhome RIP,“现在是” :) 嗯,我看到维基百科已经把这句话弄糊涂了。哦,好吧。
  • 它没有解决问题。请注意,问题出在第二个宏上,它可以评估为 printf(format, __VA_ARGS__)
  • @EduardoReis 您更新的代码表明您仍然尝试将多个预处理器指令放在同一行。这根本不是保罗的建议。
  • 哦,对了,你有两个不同的问题。我错过了,对不起。我建议您提出一个新问题,并结合我的答案中描述的修复程序,以便读者可以使用 __VA_ARGS__ 专注于您的问题。当您这样做时,请只关注该问题并显示您的宏以及您如何调用它,请参阅minimal reproducible example
【解决方案2】:

你不能使用 #ifdef inside of #define ,所以不,这是不可能的。您显示的第一个代码是正确的解决方案。

【讨论】:

    【解决方案3】:

    #define 内使用#ifdef 是不可能的。但仍有一些方法可以检测宏定义中是否定义了宏。

    1。解决方案

    godbolt

    #define CAT(a, b) CAT_IMPL(a, b)
    #define CAT_IMPL(a, b) a ## b
    
    #define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
    #define CHECK_DEBUG ~,~
    #define CHECK(tup) CHECK_IMPL tup
    #define CHECK_IMPL(a, b, c, ...) c
    
    #define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
    #define IIF_0(true_value, false_value) false_value
    #define IIF_1(true_value, false_value) true_value 
    
    #define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
    // this will be used if DEBUG is defined:
    #define PRINT_DEBUGGING(format) debugPrint(format)
    // this will be used if DEBUG is NOT defined:
    #define PRINT_NOT_DEBUGGING(format) print(format)
    

    PRINT_IF_DEBUGGING(foo) 将扩展为:

    • 如果定义了DEBUGdebugPrint(foo)
    • 如果DEBUG 定义:print(foo)

    例子:

    PRINT_IF_DEBUGGING("test1");
    
    #define DEBUG
    PRINT_IF_DEBUGGING("test2");
    #undef DEBUG
    
    PRINT_IF_DEBUGGING("test3");
    

    会导致:

    print("test1");
    
    debugPrint("test2");
    
    print("test3");
    

    2。 IS_DEBUG_DEFINED() 的工作原理

    这背后的基本技巧是使用concatenation - 如果定义了宏,它将被扩展,否则预处理器不会修改令牌:

    godbolt

    #define CAT(a, b) CAT_IMPL(a, b)
    #define CAT_IMPL(a, b) a ## b
    
    // DEBUG NOT DEFINED:
    CAT(CHECK_,DEBUG) // will expand to CHECK_DEBUG
    
    // DEBUG DEFINED:
    #define DEBUG 1234
    CAT(CHECK_,DEBUG) // will expand to CHECK_1234
    
    • 第一个 CAT 将扩展为 CHECK_DEBUG,因为未定义 DEBUG
    • 然而,第二个CAT 将扩展为CHECK_1234,因为DEBUG 在与CHECK_ 连接之前已定义并扩展为1234

    通过定义一个名为 CHECK_DEBUG 的宏,如果未定义该宏,我们可以更改结果,例如:

    godbolt

    #define TEST CAT(CHECK_,DEBUG), 0, 1
    #define CHECK_DEBUG ~,~
    
    • 如果未定义 DEBUG,则结果将是 ~, ~, 0, 14 个逗号分隔的标记)
    • 如果定义了DEBUG,则结果将为CHECK_, 0, 13 个逗号分隔的标记)

    请注意我们如何在第一种情况下获得 4 个令牌,但在第二种情况下只有 3 个令牌。

    现在我们需要做的就是从该序列中获取第三个标记(如果未定义DEBUG,则为0,否则为1),例如使用始终返回第三个参数的简单宏:

    #define CHECK(a, b, c, ...) c
    

    综合起来,这就是完整的IS_DEBUG_DEFINED() 的样子:

    godbolt

    #define CAT(a, b) CAT_IMPL(a, b)
    #define CAT_IMPL(a, b) a ## b
    
    #define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
    #define CHECK_DEBUG ~,~
    #define CHECK(tup) CHECK_IMPL tup
    #define CHECK_IMPL(a, b, c, ...) c
    

    如果DEBUG 未定义,IS_DEBUG_DEFINED() 将扩展为0,如果已定义1,例如:

    IS_DEBUG_DEFINED() // -> 0
    
    #define DEBUG
    IS_DEBUG_DEFINED() // -> 1
    #undef DEBUG
    
    IS_DEBUG_DEFINED() // -> 0
    

    使用IS_DEBUG_DEFINED(),您可以使用标准预处理器IIF 来更改宏的行为,具体取决于是否定义了DEBUG

    示例:godbolt

    #define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
    #define IIF_0(true_value, false_value) false_value
    #define IIF_1(true_value, false_value) true_value 
    
    #define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
    // this will be used if DEBUG is defined:
    #define PRINT_DEBUGGING(format) debugPrint(format)
    // this will be used if DEBUG is NOT defined:
    #define PRINT_NOT_DEBUGGING(format) print(format)
    
    
    PRINT_IF_DEBUGGING("test"); // -> print("test");
    
    #define DEBUG
    PRINT_IF_DEBUGGING("test"); // -> debugPrint("test");
    #undef DEBUG
    
    PRINT_IF_DEBUGGING("test"); // -> print("test");
    

    3。注意事项

    对此的一个小警告是,如果定义了 DEBUG,它必须扩展为有效的预处理标记(因此它必须只包含字母、数字和下划线) - 否则连接将导致一个错误。

    所以这不会工作:

    #define DEBUG ()
    #define DEBUG +
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-01
      • 2011-08-06
      • 1970-01-01
      • 2017-11-07
      • 1970-01-01
      • 2022-12-03
      • 2012-05-16
      相关资源
      最近更新 更多