有时,当使用宏来生成代码时,有必要创建具有全局范围的标识符,但这些标识符对于创建它们的直接上下文之外的任何内容都没有用。例如,假设有必要将数组或其他索引资源编译时分配到各种大小的块中。
/* Produce an enumeration of some story-book characters, and allocate some arbitrary index resource to them. Both enumeration and resource indices will start at zero. For each name, defines HPID_xxxx to be the enumeration of that name. Also defines HP_ID_COUNT to be the total number of names, and HP_TOTAL_SIZE to be the total resource requirement, and creates an array hp_starts[HP_ID_COUNT+1]. Each character n is allocated resources from hp_starts[n] through (but not including) hp_starts[n+1]. */ /* Give the names and their respective lengths */ #define HP_LIST \ HP_ITEM(FRED, 4) \ HP_ITEM(GEORGE, 6) \ HP_ITEM(HARRY, 5) \ HP_ITEM(RON, 3) \ HP_ITEM(HERMIONE, 8) \ /* BLANK LINE REQUIRED TO ABSORB LAST BACKSLASH */ #define HP_ITEM(name, length) HPID_##name, typedef enum { HP_LIST HP_ID_COUNT} HP_ID; #undef HP_ITEM #define HP_ITEM(name, length) ZZQ_##name}; enum {ZZQX_##name=ZZQ_##name+(length)-1, enum { HP_LIST HP_TOTAL_SIZE}; #undef HP_ITEM #define HP_ITEM(name, length) ZZQ_##name, const unsigned char hp_starts[] = { HP_LIST HP_TOTAL_SIZE}; #undef HP_ITEM #include "stdio.h" void main(void) { int i; printf("ID count=%d Total size=%d\n",HP_ID_COUNT,HP_TOTAL_SIZE); for (i=0; HP_ID_COUNT > i; i++) /* Reverse conditional to avoid lt sign */ printf(" %2d=%3d/%3d\n", i, hp_starts[i], hp_starts[i+1]-hp_starts[i]); printf("IDs are: \n"); #define HP_ITEM(name, length) printf(" %2d=%s\n",HPID_##name, #name); HP_LIST #undef HP_ITEMS }
是否存在命名此类标识符的常规约定,以最大限度地减少冲突的可能性,并最大限度地减少它们可能产生的混淆?在上面的场景中,标识符ZZQ_xxx将与hp_starts [HPID_xxx]相同,并且在某些上下文中可能有用,尽管它们的主要目的是构建阵列并在计算其他ZZQ值和HP_TOTAL_SIZE时用作占位符。然而,标识符ZZQX_xxx是无用的;它们的唯一目的是在为后续项设置枚举值时用作占位符。有没有什么好方法来命名这些东西?
顺便说一句,我为小型微控制器开发的RAM比代码空间更高。通过在Microsoft VC ++上编译来模拟代码,但是对于生产是使用直接C中的交叉编译器编译的。因此,代码必须在C和C ++中编译。
是否还有人可以为类似任务推荐的其他预处理技巧?
答案 0 :(得分:1)
是否存在命名此类标识符的常规约定,以最大限度地减少冲突的可能性,并最大限度地减少它们可能产生的混淆?
这一切都归结为你想要使用的前缀。理想情况下,人们希望所有符号都能与他们所关联的列表(HP_LIST
)轻松关联。
那么为什么不将符号放在相同的HP_
前缀下呢?例如。前缀HP__ZZQX_
,用于区分有用符号和无用符号。
N.B。我参与过一个项目,其中一个共享库已经在使用(内部)zzqx_
前缀,它总是出现在应用程序的符号表中。在不太可能使用的名称的竞争中,显然许多人采用相同的路线(拉丁字母的结尾)并最终得到完全相同的名称。与期望结果相反。这就是为什么我认为命名空间(或C中的符号前缀)不应该隐藏/隐藏在定义中,而是明确定义(例如易于查找和提取)。
作为具体的东西,这里是使用hack around ##
增强的源代码,使用作为预处理器定义的前缀生成名称:
/* the hack is needed to force the LIST_NAME to be expanded.
automatically adds underscores. yes, it's ugly */
#define LIST_SYMBOL_1(n1,n2,n3) n1##_##n2##_##n3
#define LIST_SYMBOL_0(n1,n2,n3) LIST_SYMBOL_1(n1,n2,n3)
#define LIST_SYMBOL(pref,name) LIST_SYMBOL_0(LIST_NAME,pref,name)
/* give the name to the list. used by the LIST_SYMBOL(). */
#define LIST_NAME HP
/* Give the names and their respective lengths */
#define HP_LIST \
HP_ITEM(FRED, 4) \
HP_ITEM(GEORGE, 6) \
HP_ITEM(HARRY, 5) \
HP_ITEM(RON, 3) \
HP_ITEM(HERMIONE, 8) \
/* BLANK LINE REQUIRED TO ABSORB LAST BACKSLASH */
#define HP_ITEM(name, length) HPID_##name,
typedef enum { HP_LIST HP_ID_COUNT} HP_ID;
#undef HP_ITEM
#define HP_ITEM(name, length) LIST_SYMBOL(ZZQ,name)}; \
enum {LIST_SYMBOL(ZZQX,name)=LIST_SYMBOL(ZZQ,name)+(length)-1,
enum { HP_LIST HP_TOTAL_SIZE};
#undef HP_ITEM
#define HP_ITEM(name, length) LIST_SYMBOL(ZZQ,name),
const unsigned char hp_starts[] = { HP_LIST HP_TOTAL_SIZE};
#undef HP_ITEM
#include <stdio.h>
void main(void)
{
int i;
printf("ID count=%d Total size=%d\n",HP_ID_COUNT,HP_TOTAL_SIZE);
for (i=0; i<HP_ID_COUNT ; i++) /* bring the < back, SO is smart enough */
printf(" %2d=%3d/%3d\n", i, hp_starts[i], hp_starts[i+1]-hp_starts[i]);
printf("IDs are: \n");
#define HP_ITEM(name, length) printf(" %2d=%s\n",HPID_##name, #name);
HP_LIST
#undef HP_ITEMS
}
修改1 。我首选的方法是将数据放入适当的文本文件中,例如:
FRED
GEORGE
HARRY
RON
HERMIONE
(请注意,您不再需要长度)并编写一个脚本(甚至是一个简单的C程序)来从文本文件生成源代码,创建必要的头文件(使用enum +数据声明)和源代码文件(包含数据)。修改Makefile以在编译任何源之前运行脚本,并将生成的源文件添加到已编译源列表中。
具有巨大优势,即生成的代码是普通代码,并且可以这样编入索引(除非你喜欢“darn id来自哪里?”的乐趣)。由于脚本处理它们,因此内部常量在源代码中不再出现。并且没有任何虚假的预处理器魔法了。
答案 1 :(得分:0)
您可以检查boost项目中的预处理器宏。他们有聪明的预处理器计数器。与__LINE__
一起,您可以使用它来生成唯一标识符,这些标识符仅取决于它们展开的行。这可以帮助您避免重新定义HP_ITEM
宏。