UPDATE: Obviously, you'd want to do this using templates or a base class rather than macros. Unfortunately for various reasons I can't use templates, or a base class.
更新:显然,您希望使用模板或基类而不是宏来执行此操作。不幸的是由于各种原因我不能使用模板或基类。
At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:
目前我正在使用宏来定义各种类的字段和方法,如下所示:
class Example
{
// Use FIELDS_AND_METHODS macro to define some methods and fields
FIELDS_AND_METHODS(Example)
};
FIELDS_AND_METHODS
is a multi-line macro that uses stringizing and token-pasting operators.
FIELDS_AND_METHODS是一个使用字符串化和令牌粘贴操作符的多行宏。
I would like to replace this with the following kind of thing
我想用以下类型的东西替换它
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"
};
Here I #define the name of the class (previously the parameter to the macro), and the FieldsNMethods.h
file contains the content of the original macro. However, because I'm #including I can step into the code at runtime, when debugging.
这里我#define类的名称(以前是宏的参数),FieldsNMethods.h文件包含原始宏的内容。但是,因为我是#including我可以在运行时进入代码,在调试时。
However I am having trouble 'stringizing' and 'token pasting' the TYPE_NAME
preprocessor symbol in the FieldsNMethods.h
file.
但是,我在FieldsNMethods.h文件中的“字符串化”和“标记粘贴”TYPE_NAME预处理程序符号时遇到问题。
For example, I want to define the destructor of the class in FieldsNMethods.h
, so this would need to use the value of TYPE_NAME
as below:
例如,我想在FieldsNMethods.h中定义类的析构函数,因此需要使用TYPE_NAME的值,如下所示:
~TYPE_NAME()
{
//...
}
But with TYPE_NAME
replaced by its value.
但TYPE_NAME替换为其值。
Is what I'm attempting possible? I can't use the stringizing and token-pasting operators directly, because I'm not in a macro definition.
我正在尝试的是什么?我不能直接使用字符串化和令牌粘贴操作符,因为我不在宏定义中。
4 个解决方案
#1
6
This cries out for a template.
这迫切需要一个模板。
class Example<class T>
{
...class definition...
};
The direct answer to the last part of your question - "given that I'm not in a macro definition any more, how do I get pasting and stringizing operators to work" - is "You can't". Those operators only work in macros, so you'd have to write macro invocations in order to get them to work.
直接回答你问题的最后一部分 - “鉴于我不再处于宏观定义中,如何将操作符号粘贴和字符串化” - 是“你不能”。这些运算符只能在宏中工作,因此您必须编写宏调用才能使它们正常工作。
Added:
@mackenir said "templates are not an option". Why are templates not an option? The code is simulating templates the old-fashioned pre-standard, pre-template way, and does so causing much pain and grief. Using templates would avoid that pain -- though there'd be a conversion operation.
@mackenir说“模板不是一种选择”。为什么模板不是一个选项?代码模拟模板是老式的预标准,预模板方式,并且这样做会造成很多痛苦和悲伤。使用模板可以避免这种痛苦 - 尽管有转换操作。
@mackenir asked "is there a way to make things work with macros?" Yes, you can, but you should use templates - they are more reliable and maintainable. To make it work with macros, then you'd have to have the function names in the code in the included header be macro invocations. You need to go through a level of indirection to get this to work correctly:
@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) { ... }
This level of indirection is an often necessary idiom for both tokenizing and stringizing operators.
对于标记化和字符串化运算符,这种间接级别通常是必需的习惯用法。
Additional comments from @mackenir indicate continued problems. Let's make it concrete.
来自@mackenir的其他评论表明仍存在问题。让我们具体化吧。
At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:
目前我正在使用宏来定义各种类的字段和方法,如下所示:
class Example
{
// Use FIELDS_AND_METHODS macro to define some methods and fields
FIELDS_AND_METHODS(Example)
};
FIELDS_AND_METHODS is a multi-line macro that uses stringizing and token-pasting operators.
FIELDS_AND_METHODS是一个使用字符串化和令牌粘贴操作符的多行宏。
I would like to replace this with the following kind of thing
我想用以下类型的东西替换它
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"
};
OK. To make this concrete, we need a FIELDS_AND_METHODS(type)
macro that is multi-line and uses token-pasting (I'm not going to deal with stringizing - the same basic mechanisms will apply, though).
好。为了使这个具体,我们需要一个多线的FIELDS_AND_METHODS(类型)宏并使用标记粘贴(我不打算处理字符串化 - 但是将应用相同的基本机制)。
#define FIELDS_AND_METHODS(type) \
type *next; \
type() : next(0) { } \
type * type ## _next() { return next; }
With luck, this declares a member of the type 'pointer to argument type', a constructor for that type, and a method (Example_next in this case) that returns that pointer.
幸运的是,这声明了“指向参数类型的指针”类型的成员,该类型的构造函数以及返回该指针的方法(在本例中为Example_next)。
So, this might be the macro - and we need to replace it such that the '#include' does the equivalent job.
所以,这可能是宏 - 我们需要替换它,以便'#include'完成相同的工作。
The content of fieldsNmethods.h becomes:
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
Note that the header would not contain multiple-inclusion guards; its raison d'etre is to allow it to be included multiple times. It also undefines its helper macros to permit multiple inclusion (well, since the redefinitions would be identical, they're 'benign' and wouldn't cause an error), and I prefixed them with FNM_
as a primitive namespace control on the macros. This generates the code I'd expect from the C pre-processor. and G++ doesn't witter but produces an empty object file (because the types declared are not used in my example code).
请注意,标题不包含多包含保护;它的存在理由是允许它被多次包括在内。它还取消定义其辅助宏以允许多个包含(好吧,因为重新定义将是相同的,它们是'良性的'并且不会导致错误),并且我将它们作为宏上的原始命名空间控件用FNM_作为前缀。这会生成我期望从C预处理器获得的代码。并且G ++不会干扰但会产生一个空的目标文件(因为我的示例代码中没有使用声明的类型)。
Note that this does not require any changes to the calling code except the one outlined in the question. I think the question should be improved using the SPOT "Single Point of Truth" principle (or DRY "Don't Repeat Yourself"):
请注意,除了问题中概述的调用代码之外,这不需要对调用代码进行任何更改。我认为应该使用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
5
You have to add an extra layer of macros:
您必须添加额外的宏层:
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define TOKENPASTE(x, y) TOKENPASTE2(x, y)
#define TOKENPASTE2(x, y) x ## y
The reason is that when you have a macro, the preprocessor normally recursively expands the arguments before performing the macro substitution. However, if any argument is used with the stringizing operator # or the token-pasting operator ##, it is not expanded. Therefore, you need an extra layer of macros, where the first layer expands the arguments, and the second layer performs the stringizing or token pasting.
原因是当你有一个宏时,预处理器通常在执行宏替换之前递归地扩展参数。但是,如果任何参数与字符串化运算符#或令牌粘贴运算符##一起使用,则不会展开它。因此,您需要一个额外的宏层,其中第一层扩展参数,第二层执行字符串化或标记粘贴。
If the arguments need to be expanded multiple times (such as #define A B, #define B C, #define C D, STRINGIZE(A)
), then you need to add that many more layers before you apply the # or ## operators.
如果参数需要多次扩展(例如#define A B,#define B C,#define C D,STRINGIZE(A)),那么在应用#或##运算符之前,需要添加更多层。
#3
3
You should wrap the stringifying with another macro (2 are necessary because of how the preprocessor works)
你应该用另一个宏包装字符串(由于预处理器的工作原理,2是必要的)
In FieldsNMethods.h
#define MAKE_STR_X( _v ) # _v
#define MAKE_STR( _v ) MAKE_STR_X( _v )
char *method() { return MAKE_STR( TYPE_NAME ); }
#4
1
No, you can't define class or function definitions on the fly. They have to be specified, either by typing them in directly or by defining them in the preprocessor.
不,您无法动态定义类或函数定义。必须通过直接键入或在预处理器中定义它们来指定它们。
Typically, there is no need to generate classes like this, and the class definitions are created before compilation, whether by typing everything in or using some sort of code generation. Sometimes there is a separate code generation step (in current Visual Studio, for example, you can define pre- and post-processing steps).
通常,不需要生成这样的类,并且在编译之前创建类定义,无论是通过键入所有内容还是使用某种代码生成。有时会有一个单独的代码生成步骤(例如,在当前的Visual Studio中,您可以定义前处理和后处理步骤)。
Now, if you needed to create different versions of some classes for different data types, you'd use templates. You can't create rubber-stamp classes of differing names that way.
现在,如果您需要为不同的数据类型创建某些类的不同版本,则可以使用模板。您不能以这种方式创建不同名称的橡皮图章类。
One last question: why are you doing this? I've never been in a position where something like that would look useful in C++, and in languages where this does make sense there are facilities to do it.
最后一个问题:你为什么要这样做?我从来没有处过这样的地方,在这种情况下,这样的东西在C ++中看起来很有用,并且在那些确实有意义的语言中有设施可以做到。
#1
6
This cries out for a template.
这迫切需要一个模板。
class Example<class T>
{
...class definition...
};
The direct answer to the last part of your question - "given that I'm not in a macro definition any more, how do I get pasting and stringizing operators to work" - is "You can't". Those operators only work in macros, so you'd have to write macro invocations in order to get them to work.
直接回答你问题的最后一部分 - “鉴于我不再处于宏观定义中,如何将操作符号粘贴和字符串化” - 是“你不能”。这些运算符只能在宏中工作,因此您必须编写宏调用才能使它们正常工作。
Added:
@mackenir said "templates are not an option". Why are templates not an option? The code is simulating templates the old-fashioned pre-standard, pre-template way, and does so causing much pain and grief. Using templates would avoid that pain -- though there'd be a conversion operation.
@mackenir说“模板不是一种选择”。为什么模板不是一个选项?代码模拟模板是老式的预标准,预模板方式,并且这样做会造成很多痛苦和悲伤。使用模板可以避免这种痛苦 - 尽管有转换操作。
@mackenir asked "is there a way to make things work with macros?" Yes, you can, but you should use templates - they are more reliable and maintainable. To make it work with macros, then you'd have to have the function names in the code in the included header be macro invocations. You need to go through a level of indirection to get this to work correctly:
@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) { ... }
This level of indirection is an often necessary idiom for both tokenizing and stringizing operators.
对于标记化和字符串化运算符,这种间接级别通常是必需的习惯用法。
Additional comments from @mackenir indicate continued problems. Let's make it concrete.
来自@mackenir的其他评论表明仍存在问题。让我们具体化吧。
At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:
目前我正在使用宏来定义各种类的字段和方法,如下所示:
class Example
{
// Use FIELDS_AND_METHODS macro to define some methods and fields
FIELDS_AND_METHODS(Example)
};
FIELDS_AND_METHODS is a multi-line macro that uses stringizing and token-pasting operators.
FIELDS_AND_METHODS是一个使用字符串化和令牌粘贴操作符的多行宏。
I would like to replace this with the following kind of thing
我想用以下类型的东西替换它
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"
};
OK. To make this concrete, we need a FIELDS_AND_METHODS(type)
macro that is multi-line and uses token-pasting (I'm not going to deal with stringizing - the same basic mechanisms will apply, though).
好。为了使这个具体,我们需要一个多线的FIELDS_AND_METHODS(类型)宏并使用标记粘贴(我不打算处理字符串化 - 但是将应用相同的基本机制)。
#define FIELDS_AND_METHODS(type) \
type *next; \
type() : next(0) { } \
type * type ## _next() { return next; }
With luck, this declares a member of the type 'pointer to argument type', a constructor for that type, and a method (Example_next in this case) that returns that pointer.
幸运的是,这声明了“指向参数类型的指针”类型的成员,该类型的构造函数以及返回该指针的方法(在本例中为Example_next)。
So, this might be the macro - and we need to replace it such that the '#include' does the equivalent job.
所以,这可能是宏 - 我们需要替换它,以便'#include'完成相同的工作。
The content of fieldsNmethods.h becomes:
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
Note that the header would not contain multiple-inclusion guards; its raison d'etre is to allow it to be included multiple times. It also undefines its helper macros to permit multiple inclusion (well, since the redefinitions would be identical, they're 'benign' and wouldn't cause an error), and I prefixed them with FNM_
as a primitive namespace control on the macros. This generates the code I'd expect from the C pre-processor. and G++ doesn't witter but produces an empty object file (because the types declared are not used in my example code).
请注意,标题不包含多包含保护;它的存在理由是允许它被多次包括在内。它还取消定义其辅助宏以允许多个包含(好吧,因为重新定义将是相同的,它们是'良性的'并且不会导致错误),并且我将它们作为宏上的原始命名空间控件用FNM_作为前缀。这会生成我期望从C预处理器获得的代码。并且G ++不会干扰但会产生一个空的目标文件(因为我的示例代码中没有使用声明的类型)。
Note that this does not require any changes to the calling code except the one outlined in the question. I think the question should be improved using the SPOT "Single Point of Truth" principle (or DRY "Don't Repeat Yourself"):
请注意,除了问题中概述的调用代码之外,这不需要对调用代码进行任何更改。我认为应该使用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
5
You have to add an extra layer of macros:
您必须添加额外的宏层:
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define TOKENPASTE(x, y) TOKENPASTE2(x, y)
#define TOKENPASTE2(x, y) x ## y
The reason is that when you have a macro, the preprocessor normally recursively expands the arguments before performing the macro substitution. However, if any argument is used with the stringizing operator # or the token-pasting operator ##, it is not expanded. Therefore, you need an extra layer of macros, where the first layer expands the arguments, and the second layer performs the stringizing or token pasting.
原因是当你有一个宏时,预处理器通常在执行宏替换之前递归地扩展参数。但是,如果任何参数与字符串化运算符#或令牌粘贴运算符##一起使用,则不会展开它。因此,您需要一个额外的宏层,其中第一层扩展参数,第二层执行字符串化或标记粘贴。
If the arguments need to be expanded multiple times (such as #define A B, #define B C, #define C D, STRINGIZE(A)
), then you need to add that many more layers before you apply the # or ## operators.
如果参数需要多次扩展(例如#define A B,#define B C,#define C D,STRINGIZE(A)),那么在应用#或##运算符之前,需要添加更多层。
#3
3
You should wrap the stringifying with another macro (2 are necessary because of how the preprocessor works)
你应该用另一个宏包装字符串(由于预处理器的工作原理,2是必要的)
In FieldsNMethods.h
#define MAKE_STR_X( _v ) # _v
#define MAKE_STR( _v ) MAKE_STR_X( _v )
char *method() { return MAKE_STR( TYPE_NAME ); }
#4
1
No, you can't define class or function definitions on the fly. They have to be specified, either by typing them in directly or by defining them in the preprocessor.
不,您无法动态定义类或函数定义。必须通过直接键入或在预处理器中定义它们来指定它们。
Typically, there is no need to generate classes like this, and the class definitions are created before compilation, whether by typing everything in or using some sort of code generation. Sometimes there is a separate code generation step (in current Visual Studio, for example, you can define pre- and post-processing steps).
通常,不需要生成这样的类,并且在编译之前创建类定义,无论是通过键入所有内容还是使用某种代码生成。有时会有一个单独的代码生成步骤(例如,在当前的Visual Studio中,您可以定义前处理和后处理步骤)。
Now, if you needed to create different versions of some classes for different data types, you'd use templates. You can't create rubber-stamp classes of differing names that way.
现在,如果您需要为不同的数据类型创建某些类的不同版本,则可以使用模板。您不能以这种方式创建不同名称的橡皮图章类。
One last question: why are you doing this? I've never been in a position where something like that would look useful in C++, and in languages where this does make sense there are facilities to do it.
最后一个问题:你为什么要这样做?我从来没有处过这样的地方,在这种情况下,这样的东西在C ++中看起来很有用,并且在那些确实有意义的语言中有设施可以做到。