【问题标题】:Naming throw-away identifiers of global scope命名全局范围的一次性标识符
【发布时间】:2011-03-29 00:49:51
【问题描述】:

有时,在使用宏生成代码时,有必要创建具有全局范围但对于创建它们的直接上下文之外的任何内容都没有真正用处的标识符。例如,假设有必要在编译时将数组或其他索引资源分配到各种大小的块中。

/* 产生一些故事书人物的枚举,并分配一些 任意索引资源给他们。枚举和资源索引 将从零开始。 对于每个名称,将 HPID_xxxx 定义为该名称的枚举。 还将 HP_ID_COUNT 定义为名称的总数,并且 HP_TOTAL_SIZE 为总资源需求,并创建一个 数组 hp_starts[HP_ID_COUNT+1]。每个字符 n 被分配资源 从 hp_starts[n] 到(但不包括)hp_starts[n+1]。 */ /* 给出名称和它们各自的长度 */ #define HP_LIST \ HP_ITEM(弗雷德,4)\ HP_ITEM(乔治,6)\ HP_ITEM(哈利,5)\ HP_ITEM(RON, 3) \ HP_ITEM(赫敏,8)\ /* 吸收最后一个反斜杠需要空白行 */ #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};枚举 {ZZQX_##name=ZZQ_##name+(长度)-1, 枚举 { 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” 无效主要(无效) { 诠释我; printf("ID 计数=%d 总大小=%d\n",HP_ID_COUNT,HP_TOTAL_SIZE); for (i=0; HP_ID_COUNT > i; i++) /* 反向条件以避免 lt 符号 */ printf(" %2d=%3d/%3d\n", i, hp_starts[i], hp_starts[i+1]-hp_starts[i]); printf("ID 是:\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++ 中编译。

对于类似的任务,人们可以推荐任何其他预处理器技巧吗?

【问题讨论】:

    标签: c++ c c-preprocessor naming


    【解决方案1】:

    是否有任何命名此类标识符的常规约定以尽量减少冲突的可能性,并尽量减少它们可能产生的任何混淆?

    这一切都归结为您要使用的前缀。理想情况下,人们希望所有符号都能轻松地与它们相关的列表 (HP_LIST) 相关联。

    那么为什么不将这些符号放在相同的HP_ 前缀下呢?例如。前缀HP__ZZQX_,区分有用和无用的符号。

    注意我从事过一个项目,其中一个共享库已经(内部)使用 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 程序)以从文本文件生成源代码,创建必要的标头(带有数据的枚举 + 声明)和源文件(带有数据)。修改 Makefile 以在编译任何源之前运行脚本,并将生成的源文件添加到已编译源的列表中。

    这具有巨大的优势,即生成的代码是纯代码并且可以这样被索引(除非你喜欢“那个该死的 id 来自哪里?”的乐趣)。内部常量不再出现在源代码中,因为脚本会处理它们。也不再有丑陋的预处理器魔法了。

    【讨论】:

    • 我认为将事物放在字母表末尾的部分想法是鼓励它们出现在符号表的末尾,远离任何其他有用的东西。有趣的是,ZZQX 会在多个地方使用。也许 ZZQX_HP_ 会是一个好的前缀?顺便说一句,你喜欢用枚举做数学的方式吗?见过吗?不得不关闭并重新打开枚举以允许将新发布的值用于计算下一个值(C 需要,而不是 C++),这有点令人讨厌,但这是预处理器中没有真正的数学运算的结果。
    • 老实说,我不得不使用几个类似的方案,但我都不喜欢它们。 (最流行的是将 HP_LIST 放入包含文件(不带斜线),然后将其包含在代码中使用 HP_LIST 的位置。)我更喜欢将数据放入适当的文本文件并编写脚本来生成源代码从文本文件。这具有 HUGE 的优势,即生成的代码是纯代码并且可以这样被索引(或者您喜欢“那个该死的 id 来自哪里?”的乐趣)。并且内部常量不再出现在源代码中。也没有丑陋的预处理器魔法。
    • 我在回复中添加了我在评论中提到的内容:我实际上更喜欢这样做。
    【解决方案2】:

    您可以检查 boost 项目中的预处理器宏。他们有聪明的预处理器计数器。与__LINE__ 一起,您可以使用它来生成唯一标识符,这些标识符仅取决于它们展开的行。这可以帮助您避免重新定义 HP_ITEM 宏。

    【讨论】:

      猜你喜欢
      • 2012-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-05
      • 1970-01-01
      • 2013-04-13
      相关资源
      最近更新 更多