C预处理器:实现自己的__COUNTER__。

5
我目前在我的C库代码中使用__COUNTER__宏来生成唯一的整数标识符。它运行良好,但我看到两个问题:
  • 它不是任何C或C++标准的一部分。
  • 也使用__COUNTER__的独立代码可能会混淆。
因此,我希望自己实现一个等效于__COUNTER__的东西。
我知道的替代方法,但不想使用的有:
  • __LINE__(因为每行多个宏将无法获得唯一的ID)
  • BOOST_PP_COUNTER(因为我不想依赖boost
BOOST_PP_COUNTER证明了这是可以做到的,尽管其他答案声称这是不可能的。
本质上,我正在寻找一个名为“mycounter.h”的头文件,使得:
#include "mycounter.h"

__MYCOUNTER__
__MYCOUNTER__ __MYCOUNTER__
__MYCOUNTER__

gcc -E将对其进行预处理,以便于

(...)

0
1 2
3

不使用内置的 __COUNTER__ 实现。

注意:早期,这个问题被标记为 这个 的重复,它涉及使用 __COUNTER__ 而不是避免使用它。


1
在某些情况下,__LINE__ 可以很好地替代 __COUNTER__ - vharavy
正如我在问题中指出的那样,__LINE__ 不足以满足我的需求。 - mic_e
2
学习BOOST_PP_COUNTER的设计,并以自己的方式重新实现它,但这比仅仅重用boost::preprocessor要困难得多。 - Jonathan Leffler
这个有帮助吗?C++支持编译时计数器吗? - Potatoswatter
@Potatoswatter:我不这么认为。虽然C++11完全可以使用,但我需要一个作为表达式一部分的宏,并且在使用时还会增加计数器。我认为你的代码无法实现这一点(但我没有花时间完全理解它)。 - mic_e
显示剩余2条评论
3个回答

3
您不能直接实现__COUNTER__。 预处理器是纯函数式的-没有状态更改。 在这样的系统中,隐藏计数器本质上是不可能的。 BOOST_PP_COUNTER 并不证明您想要的内容可以完成 - 它依赖于 #include ,因此每行只能使用一次 - 最好使用 __LINE__ 。 尽管如此,该实现非常出色,您仍应阅读它。

您可以重构元编程,使计数器可以通过纯函数应用于输入数据。例如:使用古老的 Order

#include <order/interpreter.h>

#define ORDER_PP_DEF_8map_count  \
ORDER_PP_FN(8fn(8L, 8rec_mc(8L, 8nil, 0)))

#define ORDER_PP_DEF_8rec_mc     \
ORDER_PP_FN(8fn(8L, 8R, 8C,      \
                8if(8is_nil(8L), \
                    8R,          \
                    8let((8H, 8seq_head(8L))  \
                         (8T, 8seq_tail(8L))  \
                         (8D, 8plus(8C, 1)),  \
                          8if(8is_seq(8H),    \
                              8rec_mc(8T, 8seq_append(8R, 8seq_take(1, 8L)), 8C),  \
                              8rec_mc(8T, 8seq_append(8R, 8seq(8C)), 8D) )))))

ORDER_PP (
  8map_count(8seq( 8seq(8(A)), 8true, 8seq(8(C)), 8true, 8true ))  //((A))(0)((C))(1)(2)
)

(recurses down the list, leaving sublist elements where they are and replacing non-list elements - represented by 8false - with an incrementing counter variable)
我想你可能不希望仅在程序顶层放置__COUNTER__值,因此,如果您可以将需要编织__COUNTER__值的代码放入某种序列或列表中的包装宏中,然后将该列表提供给类似于示例的纯函数,那么您可以对列表进行递归处理,其中保留子列表元素,并用一个递增的计数器变量替换非列表元素。
当然,能够表达这种代码的元编程库将比__COUNTER__更加不便携和难以维护。__COUNTER__受Intel、GCC、Clang和MSVC支持(并非每个人都支持,例如pcc就没有该功能,但是有人会使用吗?)。可以说,如果您在真实代码中展示了该功能的使用情况,则更能向标准化委员会证明__COUNTER__应该成为下一版C标准的一部分。

1
宏展开不允许状态更改或检查先前的状态,但预处理器具有状态。 - Potatoswatter
我认为你已经表明了继续使用__COUNTER__的观点。另外,哇,我甚至不知道Order的存在。 - mic_e

2
您正在混淆两件不同的事情:
1 - 预处理器处理#define#include等内容。它仅在文本(即字符序列)级别上工作,具有非常少的计算能力。它非常有限,无法实现__COUNTER__。预处理工作仅涉及宏扩展和文件替换。关键点是它发生在编译甚至开始之前。
2 - C++语言,特别是可以用于在编译阶段计算的模板(元)编程语言。它确实是图灵完备的,但正如我已经说过的,编译在预处理之后开始。
因此,您要求的在标准C或C++中无法实现。为了解决这个问题,boost实现了自己的预处理器,它不符合标准,并且具有更多的计算能力。特别是可以使用它构建类似于__counter__的模拟。

1
我完全了解CPP和模板元编程之间的区别。我已经重新命名问题以消除混淆。正如我在问题中提到的,并在这里讨论:https://dev59.com/yXA75IYBdhLWcg3wubqn#3136798,CPP是图灵完备的,因此它肯定能够做到这一点。Boost甚至使用标准CPP实现了它,从而证明它是可行的。 - mic_e
很抱歉,但是你提出的问题表述太过模糊。你想要生成什么?不同的字符串?不同的变量?正如我所解释的,你无法生成宏。 - hivert
谢谢,我已经更新了问题,希望现在更清楚了。 - mic_e
请注意,C和C++预处理器可以对常量整数表达式进行算术运算,并具有条件指令。说“预处理器的工作仅包括宏展开和文件包含”是不准确的。 - Filipe Gonçalves
@mic_e 我认为你错过了“请注意需要一个外部的构建脚本来将预处理器的输出反馈到其输入中,因此仅靠预处理器本身是不完备的”这一部分。听起来Boost做了类似的事情,所以你的选择是(1)使用Boost,或者(2)自己重新实现它。 - Kevin

1

我的这个小标题 包含了一个自己实现的 C 预处理器计数器(它使用稍微不同的语法)。


1
我给你的回答点赞是因为它原本是-1,而且没有人评论下降的原因。 - fuzzyTew
1
据我理解,使用这个库实现计数器是不可能的,因为你必须将当前位置传递给宏(例如:ZEN_COUNTER_NEXT(SECOND_VALUE)),所以你必须知道你当前在 SECOND_VALUE,而 __COUNTER__ 的作用并不是知道它。如果你能展示一个不需要当前计数器的库的使用示例,那么我会点赞。 - LoPiTaL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接