【问题标题】:C++ Stop Preprocessor Macro ExpansionC++ 停止预处理器宏扩展
【发布时间】:2019-05-08 16:57:39
【问题描述】:

这是我的示例代码https://godbolt.org/z/VKgKik

#define delete MyCustomDelete(__FILE__, __LINE__), delete

#define CAT(X,Y) CAT2(X,Y)
#define CAT2(X,Y) X##Y
#define CAT_3(X,Y,Z) CAT(X,CAT(Y,Z))    


class A {
    A() = CAT_3(de,le,te);
};

godbolt 示例设置为显示预处理器输出。目标是在预处理程序结束时,我希望输出代码是

class A {
    A() = delete;
};

目前“ThisShouldNotshowUp”会显示在此处。我认为使用 ## 运算符会阻止预处理器重新扩展,但它没有。

我意识到删除“#define delete”可以解决问题,但我需要这个定义。我创建一个与 delete 同名的宏的原因是因为我希望能够跟踪新闻和删除,如果发生内存泄漏,我可以看到哪一行代码分配了它。因此,这个宏意味着我可以继续在我的代码中使用关键字 delete,并且可以免费填写文件和行号。据我所知,除了定义一个删除宏之外,没有其他方法可以实现此功能。这是问题的症结所在。删除宏为我提供了强大的调试工具,但它删除了一个有用的语言功能供我使用。

【问题讨论】:

  • 1. CAT_2_helper 从未使用过。 2. #define delete 使程序格式错误;不允许将关键字定义为宏名称。
  • 我知道这在技术上是违反标准的,但是我可以做些什么来让它工作吗?让我们假设删除#define delete 是不可能的
  • 在我看来像XY problem。为什么需要这个?
  • 已更新,希望能解决您的问题
  • @RemyLebeau 我认为原因是一旦进入operator delete,您将无法再访问delete 表达式所在的源文件和行。

标签: c++ c++11 preprocessor preprocessor-directive


【解决方案1】:

您没有机会通过扩展宏创建预处理标记,该标记是类对象宏的名称。 n3337 的相关部分是[cpp.rescan]。我在其中引用了第一段的缩短部分。

在替换列表中的所有参数都被替换并且### 处理已经发生之后[...]。然后重新扫描生成的预处理标记序列 [...] 以替换更多宏名称。

尽管存在问题,delete 在技术上被禁止作为宏名称,但没有办法防止在重新扫描时识别宏名称。

您可能混淆了 ## 运算符确实使用它的 parameters 而不进行扩展的事实,以及 ##result 不进行宏扩展的想法.

【讨论】:

  • 包含自己名称的宏不会自行重新展开。他们在这里这样做是因为他通过嵌套 CAT 中的许多其他宏扩展来传递它。他的宏没有扩展为“ThisShouldNotshowUp”,而是“MyCustomDelete("main.c", 10), delete"。如果宏是“#define delete delete”,它不会以任何方式循环,只会留下“delete”。然而,他当然不能特殊情况下以不同方式扩展令牌宏。如果他使用宏之类的函数,则可能会发生一些事情,但这不会捕获数组删除
  • @MichaelSpeer 即使这个delete 也不会重新展开;问题不在于任何循环,而在于MyCustomDelete 在语法上无效,其中delete 用于标记已删除的函数。
  • 绝对是。我只是想确保有关宏如何扩展的信息准确无误。
【解决方案2】:

您尝试做的事情是不可能的,因为Michael Karcher's answer 指出:#define delete 已经使程序格式错误,并且无法避免扩展类对象宏(在其自身扩展之外)。

但是,对于问题中详述的特定用例,可以使用解决方法。你可以把你的#define delete放到一个头文件中(我们称之为debug_delete.hxx),像这样:

#ifdef delete
# undef delete
#endif
#define delete MyCustomDelete(__FILE__, __LINE__), delete

然后,创建另一个头文件(我们称之为normal_delete.hxx):

#ifdef delete
# undef delete
#endif

请特别注意,这些标头中没有防止多重包含的机制;事实上,我们希望它们可以包含任意次数。

然后,将必须使用= delete; 的代码包装在适当的#include 指令中:

class A {
#include "normal_delete.hxx"
    A() = delete;
#include "debug_delete.hxx"
    ~A() { delete p; }
};

(是的,它很丑陋,但你所做的首先有点丑陋,因此可能需要丑陋的代码才能使其工作)。

【讨论】:

    【解决方案3】:

    大概您想使用宏来打开和关闭删除跟踪。如果您只是在源代码上使用它,而不是尝试将其装配到转换现有 C++ 中,则可以使用类似函数的宏来实现所需的可选跟踪。

    #define TRACK_DELETES 0
    #if TRACK_DELETES
      #define DELETE( a ) \
        do { MyCustomDelete( __FILE__, __LINE__ ); delete (a); } while (0)
      #define DELETEALL( a ) \
        do { MyCustomDelete( __FILE__, __LINE__ ); delete [] (a); } while (0)
    #else
      #define DELETE( a ) do { delete (a) ; } while(0)
      #define DELETEALL( a ) do { delete [] (a) ; } while(0)
    #endif
    
    int main(){
    
      DELETE( A );
      DELETEALL( B );
    
      return 0;
    }
    

    gcc -E 下将 TRACK_DELETES 设置为 0 或 1,看看这是否符合您的要求。

    您需要单独保留裸露的 delete 关键字,以便正确使用它。

    【讨论】:

    • 这个问题是它要求编码者记住使用这个删除宏
    猜你喜欢
    • 2018-01-04
    • 2020-10-18
    • 1970-01-01
    • 2020-09-18
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多