当前位置:  开发笔记 > 编程语言 > 正文

如何用#include替换此预处理器宏?

如何解决《如何用#include替换此预处理器宏?》经验,为你挑选了2个好方法。

更新: 显然,您希望使用模板或基类而不是宏来执行此操作.不幸的是由于各种原因我不能使用模板或基类.


目前我正在使用宏来定义各种类的一堆字段和方法,如下所示:

class Example
{
  // Use FIELDS_AND_METHODS macro to define some methods and fields
  FIELDS_AND_METHODS(Example)
};

FIELDS_AND_METHODS 是一个使用字符串化和令牌粘贴操作符的多行宏.

我想用以下类型的东西替换它

class Example
{
  // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
  // defined, to achieve the same result as the macro.
  #define TYPE_NAME Example
  #include "FieldsNMethods.h"
};

这里我#define类的名称(以前是宏的参数),FieldsNMethods.h文件包含原始宏的内容.但是,因为我是#including我可以在运行时进入代码,在调试时.

但是我TYPE_NAMEFieldsNMethods.h文件中" 预处理"和"标记粘贴" 预处理程序符号时出现问题.

例如,我想定义类的析构函数FieldsNMethods.h,所以这需要使用如下的值TYPE_NAME:

~TYPE_NAME()
{
  //...
}

TYPE_NAME取而代之的是它的价值.

我正在尝试的是什么?我不能直接使用字符串化和令牌粘贴操作符,因为我不在宏定义中.



1> Jonathan Lef..:

这迫切需要一个模板.

class Example
{
    ...class definition...
};

直接回答你问题的最后一部分 - "鉴于我不再处于宏定义中,如何将操作符号粘贴和字符串化" - 是"你不能".这些运算符只能在宏中工作,因此您必须编写宏调用才能使它们工作.

补充:

@mackenir说"模板不是一种选择".为什么模板不是一个选项?代码模拟模板是老式的预标准,预模板方式,并且这样做会造成很多痛苦和悲伤.使用模板可以避免这种痛苦 - 尽管有转换操作.

@mackenir问道"有没有办法让宏工作?" 是的,你可以,但你应该使用模板 - 它们更可靠和可维护.要使它适用于宏,那么您必须在包含的标头中的代码中使用函数名称进行宏调用.您需要通过间接级别才能使其正常工作:

#define PASTE_NAME(x, y) PASTE_TOKENS(x, y)
#define PASTE_TOKENS(x, y) x ## y

#define TYPE_NAME Example
int PASTE_NAME(TYPE_NAME, _function_suffix)(void) { ... }

对于标记化和字符串化运算符,这种间接级别通常是必需的习惯用语.


来自@mackenir的其他评论表明仍存在问题.让我们具体化吧.

目前我正在使用宏来定义各种类的一堆字段和方法,如下所示:

class Example
{
    // Use FIELDS_AND_METHODS macro to define some methods and fields
    FIELDS_AND_METHODS(Example)
};

FIELDS_AND_METHODS是一个使用字符串化和令牌粘贴操作符的多行宏.

我想用以下类型的东西替换它

class Example
{
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
    // defined, to achieve the same result as the macro.
    #define TYPE_NAME Example
    #include "FieldsNMethods.h"
};

好.为了使这个具体化,我们需要一个FIELDS_AND_METHODS(type)多行的宏并使用令牌粘贴(我不会处理字符串化 - 但是同样的基本机制将适用).

#define FIELDS_AND_METHODS(type) \
    type *next; \
    type() : next(0) { } \
    type * type ## _next() { return next; }

幸运的是,这声明了"指向参数类型的指针"类型的成员,该类型的构造函数以及返回该指针的方法(在本例中为Example_next).

所以,这可能是宏 - 我们需要替换它,以便'#include'完成相同的工作.

fieldsNmethods.h的内容变为:

#ifndef TYPE_NAME
#error TYPE_NAME not defined
#endif
#define FNM_PASTE_NAME(x, y)    FNM_PASTE_TOKENS(x, y)
#define FNM_PASTE_TOKENS(x, y)  x ## y

TYPE_NAME *next;
TYPE_NAME() : next(0) { }
TYPE_NAME * FNM_PASTE_NAME(TYPE_NAME, _next)() { return next; }

#undef FNM_PASTE_NAME
#undef FNM_PASTE_TOKENS

请注意,标题不包含多包含保护; 它的存在理由是允许它被多次包括在内.它还取消定义其辅助宏以允许多个包含(好吧,因为重新定义将是相同的,它们是'良性的'并且不会导致错误),并且我将它们FNM_作为宏的原始命名空间控件作为前缀.这会生成我期望从C预处理器获得的代码.并且G ++不会干扰但会产生一个空的目标文件(因为我的示例代码中没有使用声明的类型).

请注意,除了问题中概述的调用代码之外,这不需要对调用代码进行任何更改.我认为应该使用SPOT"单点真相"原则(或干"不要重复自己")改进这个问题:

#define TYPE_NAME Example
class TYPE_NAME
{
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
    // defined, to achieve the same result as the macro.
    #include "FieldsNMethods.h"
};


你使用宏吗?这听起来很邪恶!难道你不能写一个如果外观只暴露你想要的客户端和内部有可维护?

2> Adam Rosenfi..:

您必须添加额外的宏层:

#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x

#define TOKENPASTE(x, y) TOKENPASTE2(x, y)
#define TOKENPASTE2(x, y) x ## y

原因是当你有一个宏时,预处理器通常在执行宏替换之前递归地扩展参数.但是,如果任何参数与字符串化运算符#或令牌粘贴运算符##一起使用,则不会展开它.因此,您需要一个额外的宏层,其中第一层扩展参数,第二层执行字符串化或标记粘贴.

如果参数需要多次扩展(例如#define A B, #define B C, #define C D, STRINGIZE(A)),那么在应用#或##运算符之前,需要添加更多层.

推荐阅读
手机用户2502852037
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有