C++ 停止预处理器宏扩展

C++ Stop Preprocessor Macro Expansion

这是我的示例代码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,并且免费填写文件和行号。据我所知,除了定义删除宏外,没有其他方法可以实现此功能。这就是问题的症结所在。删除宏为我提供了一个强大的调试工具,但它删除了一个有用的语言功能供我使用。

您没有机会通过扩展宏来创建作为 object-like 宏名称的预处理标记。 n3337的相关部分是[cpp.rescan]。我引用了其中第一段的缩短部分。

After all parameters in the replacement list have been substituted and # and ## processing has taken place [...]. Then the resulting preprocessing token sequence is rescanned [...] for more macro names to replace.

尽管存在问题,delete 在技术上是禁止作为宏名称的,但无法阻止在重新扫描时识别宏名称。

您可能混淆了 ## 运算符确实使用它的 参数 而没有扩展的事实与 结果 ## 没有进行宏扩展。

您尝试执行的操作是不可能的,正如 Michael Karcher's answer 所述:#define delete 已经使程序 ill-formed 和扩展 object-like 宏(外部它自己的扩展)是不可避免的。

但是,对于问题中详述的特定用例,可能有解决方法。您可以将您的 #define delete 放入 header 文件(我们称之为 debug_delete.hxx),如下所示:

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

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

#ifdef delete
# undef delete
#endif

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

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

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

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

您可能想使用宏来打开和关闭删除跟踪。如果您只在源代码上使用它,而不是试图将其装配起来以转换现有的 C++,则可以使用 function-like 宏来实现您想要的可选跟踪。

#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 关键字,以便可以适当地使用它。