【问题标题】:How do I temporarily disable a macro expansion in C/C++?如何在 C/C++ 中临时禁用宏扩展?
【发布时间】:2010-12-05 08:24:21
【问题描述】:

出于某种原因,我需要暂时禁用头文件中的一些宏,#undef MACRONAME 将使代码编译,但它会取消现有宏的定义。

有没有办法禁用它?

我应该提到你并不真正知道宏的值,我正在寻找一个交叉编译器解决方案(至少应该在 GCC 和 MSVC 中工作)。

【问题讨论】:

  • 我想你有一个令人信服的理由让你最终陷入这个宏观混乱?我想不出这里...

标签: c++ visual-c++ gcc c-preprocessor


【解决方案1】:

仅使用标准 C(C89、C99 或 C11)定义的工具,唯一的“禁用”机制是 #undef

问题是没有“重新启用”机制。


正如其他人指出的那样,如果包含宏定义的头文件的结构使其不包含任何 typedef 或 enum 声明(这些不能重复;函数和变量声明可以重复),那么您可以 @987654322 @ 宏,在宏不生效的情况下执行您需要的操作,然后重新包含标头,可能在取消定义其对重新包含的保护之后。

当然,如果宏没有在标头中定义,那么在重构代码以使它们位于标头中之前,您将陷入困境。

还有一个技巧可用 - 如果宏是类函数宏而不是类对象宏。

#define nonsense(a, b)   b /\= a

int (nonsense)(int a, int b)
{
    return (a > b) ? a : b;
}

函数nonsense() 定义良好,尽管它前面有宏。这是因为宏调用 - 对于类似函数的宏 - 必须紧跟一个左括号(给或取空格,可能包括 cmets)。在函数定义行中,'nonsense'后面的记号是一个右括号,所以它不是nonsense宏的调用。

如果宏是一个无参数的类对象宏,那么这个技巧就行不通了:

#define nonsense min

int (nonsense)(int a, int b)
{
    // Think about it - what is the function really called?
    return (a > b) ? a : b;
}

这段代码定义了一个名为min 的虚假函数,而且是荒谬的。并且没有宏的保护。

这是标准谨慎定义为“实现”保留哪些命名空间的原因之一。允许实现为它想要或需要的任何目的、它想要或需要的任何类型(类似函数或类似对象)定义宏,只要这些名称保留给实现。如果您作为 The Implementation 服务的消费者尝试使用或定义为实现保留的名称,您必须意识到您的代码迟早会中断,这将是您的错,而不是 The Implementation 的错实施。

【讨论】:

    【解决方案2】:

    宏来自一些头文件,因此您应该可以访问它们的值。然后你可以做类似的事情

    #include <foo.h> // declares macro FOO
    
    // Do things with FOO
    
    #undef FOO
    
    // do things without FOO
    
    #include <foo.h> // reenable FOO
    

    你的标题应该按照这些思路设计

    #ifndef FOO
    #define FOO do_something(x,y)
    #endif
    

    【讨论】:

    • 确保在该头文件中没有您要启用/禁用的宏以外的其他内容,否则重新声明会出现问题。
    • 还要确保&lt;foo.h&gt; 不包含#ifndef FOO_H_INCLUDED / #define FOO_H_INCLUDED / ... / #endif 以防止被重新纳入。
    • 重新包含头文件可能会导致其他问题,除非头文件专门设计用于这种方式。
    • 这是不可接受的另一个原因是因为它可能发生甚至不知道这是在哪里定义的。此外,重新包含该文件很可能不起作用。
    【解决方案3】:

    编辑:

    你可能认为这很容易:

    #ifdef macro
    #define DISABLED_macro macro
    #undef macro
    #endif
    
    // do what you want with macro
    
    #ifdef DISABLED_macro
    #define macro DISABLED_macro
    #endif
    

    但事实并非如此(如下例所示)!

    #include <iostream>
    #include <limits>
    
    #include <windows.h>
    
    #ifdef max
    #define DISABLED_max max
    #undef max
    #endif
    
    int main()
    {
        std::cout << std::numeric_limits<unsigned long>::max() << std::endl;
    
    #ifdef DISABLED_max
    #define max DISABLED_max
    #endif
    
        std::cout << max(15,3) << std::endl;  // error C3861: "max": identifier not found
        return 0;
    }
    

    在宏上使用#undef 并重新包含原始标题也不太可能工作,因为标题保护。 所以剩下的就是使用push_macro/pop_macro #pragma 指令。

    #pragma push_macro("MACRO")
    #undef MACRO
    // do what you want
    #pragma pop_macro("MACR")
    

    【讨论】:

    • 除了它有用吗?它不是“恢复”宏,因此它将扩展为“宏”(这是我在添加缺失部分时看到的)?可能是一个完整的用法示例?
    【解决方案4】:

    宏让我的膝盖发软,但最通用的解决方案不是重组代码,这样您就不需要在同一个源文件中再次重新启用宏吗?是否可以将一些代码提取到单独的函数和单独的源文件中,您可以在其中取消有问题的宏。

    【讨论】:

    • 抱歉,有时您无法修改代码来解决此问题。
    • 我想,看看其他答案,在一般情况下“重新启用”宏的唯一方法是复制它们的原始定义。
    【解决方案5】:

    在 MSVC 中,您可以使用 push_macro pragma、GCC supports 它来与 Microsoft Windows 编译器兼容。

    #pragma push_macro("MACRONAME")
    #undef MACRONAME
    
    // some actions
    
    #pragma pop_macro("MACRONAME")
    

    【讨论】:

    • 很高兴知道它的存在,以及很好的答案,但这太可怕了。
    • 这就是为什么人们不断告诉我们诸如宏是邪恶的之类的事情的原因。迟早有人会创建一个名为 new 的宏来做一些愚蠢的事情......
    • @JohnLeidegren 好吧,我们现在已经完成了一半。 Example of a misconstrued "evil" macro.
    • 也适用于clang。丑陋,但有助于解决由您无法控制的库引起的命名冲突。
    • 有人已经创建了一个名为new的宏!那是cpputest:#define new new(__FILE__, __LINE__) 现在我不能在测试中使用placement new...
    【解决方案6】:

    在 C/C++ 语言中调用类似函数的宏有特定的规则。 类函数宏必须通过以下方式调用:

    1. 宏名
    2. 左括号
    3. 每个参数一个标记,用逗号分隔

    此列表中的每个标记都可以通过空格(即实际的空格和逗号)与另一个分隔


    通过一个技巧,您可以“禁用预处理器机制”,打破类似函数的宏调用规则,但仍处于函数调用机制的规则范围内...

    #include <iostream>
    using namespace std;
    
    inline const char* WHAT(){return "Hello from function";}
    
    #define WHAT() "Hello from macro"
    
    int main()
    {
        cout << (*WHAT)() << "\n"; // use function
        cout << (WHAT)() << "\n";  // use function
        cout << WHAT () << "\n";   // use macro
        
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 2021-09-09
      • 2022-01-05
      相关资源
      最近更新 更多