【问题标题】:How can I generate unique values in the C preprocessor?如何在 C 预处理器中生成唯一值?
【发布时间】:2010-11-11 02:10:38
【问题描述】:

我正在编写一堆相关的预处理器宏,其中一个生成标签,另一个跳转到。我以这种方式使用它们:

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL();
  MAKE_LABEL();
)

我需要一些方法来生成唯一标签,每个内部 MAKE_LABEL 调用一个,使用预处理器。我试过使用__LINE__,但由于我在另一个宏中调用MAKE_LABEL,它们都有相同的行并且标签会发生冲突。

我希望将其扩展为:

MAKE_FUNNY_JUMPING_LOOP(
  my_cool_label_1:  // from first inner macro
  ...
  my_cool_label_2:  // from second inner macro
  ...
)

有没有办法使用预处理器生成散列或自增整数?

【问题讨论】:

标签: macros c-preprocessor


【解决方案1】:

我想不出一种自动生成它们的方法,但您可以将参数传递给 MAKE_LABEL:

#define MAKE_LABEL(n) my_cool_label_##n:

那么……

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL(0);
  MAKE_LABEL(1);
)

【讨论】:

  • 当然它也可以包含 LINE 宏作为 MAKE_LABEL() 定义的一部分,这样你的其他宏仍然可以使用它(只要你不使用那些宏在其他一些宏中不止一次...)
  • 好电话。实际上,我已经在为MAKE_FUNNY_JUMPING_LOOP 宏执行此操作,因为这些宏相对较少且易于描述。我想如果它只是一个,我可以处理它。但是,像这样将手动方法传播到每个宏对我来说太过分了。
【解决方案2】:

使用标准预处理器似乎是不可能的,尽管您可以通过在 MAKE_LABEL 或 MAKE_FUNNY_JUMPING_LOOP 中放置参数来伪造它,并使用令牌粘贴来创建标签。

没有什么可以阻止您制作自己的预处理脚本来为您自动递增。但是,在这种情况下,它不会是标准的 C/C++ 文件。

可用命令列表:http://www.cppreference.com/wiki/preprocessor/start

【讨论】:

    【解决方案3】:

    如果您使用的是 GCC 或 MSVC,则有 __COUNTER__

    除此之外,你还可以做一些值得吐槽的事情,比如:

    #ifndef USED_1
    #define USED_1
    1
    #else
    #ifndef USED_2
    #define USED_2
    2
    /* many many more */
    #endif
    #endif
    

    【讨论】:

    • 不错的发现。每天学习新东西。
    • 他需要注意不要在任何其他编译单元中使用它吗?否则,它可能会意外跳过数字。
    • @Jesse:每个编译单元都有自己的预处理器运行,所以 COUNTER 应该总是从 0 开始。但是,其他人很有可能使用 COUNTER 在同一个编译单元中,导致序列中的漏洞。
    • .... 序列中的漏洞,如果您想要唯一值(简单的值),这可能是一件好事,(但您确实应该使用 UUID 来做到这一点),但当然不是你想要什么...
    【解决方案4】:

    正如其他人所说,__COUNTER__ 是一种简单但非标准的方法。

    如果您需要额外的可移植性,或其他酷炫的预处理器技巧,Boost Preprocessor library(适用于 C 和 C++)将起作用。例如,以下头文件将输出一个唯一的标签,无论它被包含在哪里。

    #include <boost/preprocessor/arithmetic/inc.hpp>
    #include <boost/preprocessor/slot/slot.hpp>
    
    #if !defined(UNIQUE_LABEL)
    #define UNIQUE_LABEL
    #define BOOST_PP_VALUE 1
    #include BOOST_PP_ASSIGN_SLOT(1)
    #undef BOOST_PP_VALUE
    #else
    #define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
    #include BOOST_PP_ASSIGN_SLOT(1)
    #undef BOOST_PP_VALUE
    #endif
    
    
    BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)):
    

    示例:

    int main(int argc, char *argv[]) {
        #include "unique_label.h"
        printf("%x\n", 1234);
        #include "unique_label.h"
        printf("%x\n", 1234);
        #include "unique_label.h"
        return 0;
    }
    

    预处理到

    int main(int argc, char *argv[]) {
        my_cool_label_1:
        printf("%x\n", 1234);
        my_cool_label_2:
        printf("%x\n", 1234);
        my_cool_label_3:
        return 0;
    }
    

    【讨论】:

    • 这是一个很好的答案!感谢指点;我承认我发现 Boost.Preprocessor 文档有点密集。
    • 我也觉得它很密集。我通常需要稍微调整一下语法才能找到有用的东西,但它确实很酷。
    【解决方案5】:

    你可以这样做:

    #define MAKE_LABEL() \
    do {                 \   
    my_cool_label:       \
    /* some stuff */;    \
    goto my_cool_label;  \
    /* other stuff */;   \
    } while (0) 
    

    这将标签的范围保持在本地,允许在主宏中使用任意数量的标签。

    如果您希望更全局地访问标签,则不清楚您的宏 "MAKE_FUNNY_JUMPING_LOOP" 如何引用这些标签。你能解释一下吗?

    【讨论】:

      【解决方案6】:

      我用这个:

      #define MERGE_(a,b)  a##b
      #define LABEL_(a) MERGE_(unique_name_, a)
      #define UNIQUE_NAME LABEL_(__LINE__)
      
      int main()
      {
          int UNIQUE_NAME = 1;
          return 0;
      }
      

      ... 并得到以下信息:

      int main()
      {
          int unique_name_8 = 1;
          return 0;
      }
      

      【讨论】:

      • 你如何实际使用这个变量?你不能。
      • ....您需要在宏中添加一个非唯一名称才能访问该唯一名称,这使您回到第一方...似乎我们对预处理器太疯狂了并开始思考它可以为我们完成所有工作的方法,无论多么不切实际……更不用说耗时了。 (我,我输入了一些独特的东西然后继续……呵呵)但我认为 MSVC/GCC 的 COUNTER 解决方案很好,如果你使用的是其他东西,那么使用 boost 的东西……前两个答案很好
      猜你喜欢
      • 2011-01-14
      • 2011-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多