能否根据宏的内容定义一个新宏?
举个例子:
#define SET(key,value) #define key value
SET(myKey,"value")
int main(){
char str[] = myKey;
printf("%s",str);
}
会导致
int main(){
char str[] = "value";
printf("%s",str);
}
在进行预处理之后。
为什么要这样做?因为我很好奇 ;)
能否根据宏的内容定义一个新宏?
举个例子:
#define SET(key,value) #define key value
SET(myKey,"value")
int main(){
char str[] = myKey;
printf("%s",str);
}
int main(){
char str[] = "value";
printf("%s",str);
}
在进行预处理之后。
为什么要这样做?因为我很好奇 ;)
#define
之前的文本,则需要使用递归,但是如果代码从那一点开始生效,则似乎没有问题。 - Linsey#
,这似乎就是情况。 - Willy#define
不能被实现在标准中。看起来并没有递归问题,你能提供一个需要递归的C代码示例吗? - Linsey不行 - 在宏的替换列表中,#表示引用下一个标记。这更像是一个拼写问题,而不是逻辑难题 :)
(如果您需要在代码中使用此类解决方案,则有使用宏的方法和技巧,但您需要具体说明所需的用例 - 因为您的示例可以通过定义实现:#define mykey“value”)
这是来自ANSI C99标准的内容。
6.10.3.2 #运算符 限制条件 1.函数宏替换列表中每个#预处理标记后面必须跟随一个参数作为替换列表中的下一个预处理标记。 2.如果在替换列表中,一个参数紧接着被#预处理符号,则两者都被替换为一个单字符字符串字面量预处理标记,其中包含相应参数的预处理标记序列的拼写。在参数的预处理标记之间出现的任何空格都会在字符字符串字面量中变成一个空格字符。组成参数的第一个预处理标记之前和最后一个预处理标记之后的空格将被删除。否则,在字符字符串字面量中,保留参数中每个预处理标记的原始拼写,除了用于生成字符串字面量和字符常量拼写的特殊处理:在字符常量或字符串字面量(包括定界“字符)的每个"和\字符之前插入一个\字符,但是在普通字符名称之前开始的\字符是否插入一个\字符是由实现定义的。如果结果替换不是有效的字符字符串字面量,则行为是未定义的。对应于空参数的字符字符串字面量为""。#和##操作符的评估顺序是未指定的。虽然不可能使用宏定义另一个宏,但根据你想要实现的目标,你可以使用宏将它们定义为常量以有效地实现相同的功能。例如,我有一个广泛的 C 宏库,用于定义 Objective C 的常量字符串和键值。
以下是我的某些头文件中的代码片段。
// use defineStringsIn_X_File to define a NSString constant to a literal value.
// usage (direct) : defineStringsIn_X_File(constname,value);
#define defineStringsIn_h_File(constname,value) extern NSString * const constname;
#define defineStringsIn_m_File(constname,value) NSString * const constname = value;
// use defineKeysIn_X_File when the value is the same as the key.
// eg myKeyname has the value @"myKeyname"
// usage (direct) : defineKeysIn_X_File(keyname);
// usage (indirect) : myKeyDefiner(defineKeysIn_X_File);
#define defineKeysIn_h_File(key) defineStringsIn_h_File(key,key)
#define defineKeysIn_m_File(key) defineStringsIn_m_File(key,@#key)
// use defineKeyValuesIn_X_File when the value is completely unrelated to the key - ie you supply a quoted value.
// eg myKeyname has the value @"keyvalue"
// usage: defineKeyValuesIn_X_File(keyname,@"keyvalue");
// usage (indirect) : myKeyDefiner(defineKeyValuesIn_X_File);
#define defineKeyValuesIn_h_File(key,value) defineStringsIn_h_File(key,value)
#define defineKeyValuesIn_m_File(key,value) defineStringsIn_m_File(key,value)
// use definePrefixedKeys_in_X_File when the last part of the keyname is the same as the value.
// eg myPrefixed_keyname has the value @"keyname"
// usage (direct) : definePrefixedKeys_in_X_File(prefix_,keyname);
// usage (indirect) : myKeyDefiner(definePrefixedKeys_in_X_File);
#define definePrefixedKeys_in_h_File_2(prefix,key) defineKeyValuesIn_h_File(prefix##key,@#key)
#define definePrefixedKeys_in_m_File_2(prefix,key) defineKeyValuesIn_m_File(prefix##key,@#key)
#define definePrefixedKeys_in_h_File_3(prefix,key,NSObject) definePrefixedKeys_in_h_File_2(prefix,key)
#define definePrefixedKeys_in_m_File_3(prefix,key,NSObject) definePrefixedKeys_in_m_File_2(prefix,key)
#define definePrefixedKeys_in_h_File(...) VARARG(definePrefixedKeys_in_h_File_, __VA_ARGS__)
#define definePrefixedKeys_in_m_File(...) VARARG(definePrefixedKeys_in_m_File_, __VA_ARGS__)
// use definePrefixedKeyValues_in_X_File when the value has no relation to the keyname, but the keyname has a common prefixe
// eg myPrefixed_keyname has the value @"bollocks"
// usage: definePrefixedKeyValues_in_X_File(prefix_,keyname,@"bollocks");
// usage (indirect) : myKeyDefiner(definePrefixedKeyValues_in_X_File);
#define definePrefixedKeyValues_in_h_File(prefix,key,value) defineKeyValuesIn_h_File(prefix##key,value)
#define definePrefixedKeyValues_in_m_File(prefix,key,value) defineKeyValuesIn_m_File(prefix##key,value)
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 11, 10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL2(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL2(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
以及调用它的使用示例:
#define sw_Logging_defineKeys(defineKeyValue) \
/** start of key list for sw_Logging_ **/\
/**/defineKeyValue(sw_Logging_,log)\
/**/defineKeyValue(sw_Logging_,time)\
/**/defineKeyValue(sw_Logging_,message)\
/**/defineKeyValue(sw_Logging_,object)\
/**/defineKeyValue(sw_Logging_,findCallStack)\
/**/defineKeyValue(sw_Logging_,debugging)\
/**/defineKeyValue(sw_Logging_,callStackSymbols)\
/**/defineKeyValue(sw_Logging_,callStackReturnAddresses)\
/** end of key list for sw_Logging_ **/
sw_Logging_defineKeys(definePrefixedKeys_in_h_File);
最后一部分可能有点难以理解。 sw_Logging_defineKeys()宏定义了一个列表,该列表以宏的名称作为其参数(defineKeyValue),然后用于调用执行实际定义过程的宏。即,对于列表中的每个项目,传递的宏名称用于定义上下文(“头文件”或“实现文件”,例如,如果您了解Objective-C文件扩展名,则为“h”或“m”文件)。虽然这用于Objective-C,但它只是普通的C宏,用于比可能Kernighan and Richie曾经设想的更高目的。 :-)
宏是一种简单的文本替换。从宏生成新的预处理指令需要预处理器从替换的开头继续预处理。然而,标准定义了预处理应该在替换后面继续。
从流的角度来看,这是有道理的,将未处理的代码视为输入流,处理(和替换)的代码视为输出流。宏替换可以具有任意长度,这意味着对于从开头进行的预处理,必须在输入流的开头插入任意数量的字符以再次处理。
当处理在替换后继续时,输入只需在一个单独的运行中处理,无需插入或缓冲,因为所有内容直接进入输出。