【问题标题】:C Preprocessor: Own implementation for __COUNTER__C 预处理器:__COUNTER__ 的自己的实现
【发布时间】:2014-03-27 16:26:12
【问题描述】:

我目前在我的 C 库代码中使用 __COUNTER__ 宏来生成唯一的整数标识符。它工作得很好,但我发现了两个问题:

  • 它不属于任何 C 或 C++ 标准。
  • 也使用__COUNTER__ 的独立代码可能会混淆。

因此,我希望自己实现与__COUNTER__ 等效的功能。

我知道但不想想使用的替代方法:

  • __LINE__(因为每行多个宏不会获得唯一的 ID)
  • BOOST_PP_COUNTER(因为我不想要 boost 依赖)

BOOST_PP_COUNTER 证明这是可以做到的,尽管other 的回答声称这是不可能的。

本质上,我正在寻找一个头文件“mycounter.h”,这样

#include "mycounter.h"

__MYCOUNTER__
__MYCOUNTER__ __MYCOUNTER__
__MYCOUNTER__

会被gcc -E预处理到

(...)

0
1 2
3

不使用内置的__COUNTER__

注意:之前,这个问题被标记为与this 重复,它处理使用__COUNTER__ 而不是避免它。

【问题讨论】:

  • 在某些情况下__LINE__ 可能是__COUNTER__ 的良好替代品。
  • 正如我在问题中指出的那样,__LINE__ 不足以满足我的目的。
  • 研究 BOOST_PP_COUNTER 的设计并以自己的方式重新实现它,但这比仅重用 boost::preprocessor 更难。
  • @Hans Passant - 看起来潜在的重复使用 __COUNTER__ 本身。我认为 mic_e 试图避免这种情况,因为他声明“不使用内置的__COUNTER__”。
  • 同意,这个问题不是重复的。

标签: c macros c-preprocessor


【解决方案1】:

你不能直接实现__COUNTER__。预处理器纯粹是功能性的——没有状态变化。在这样的系统中,隐藏计数器本质上是不可能的。 (BOOST_PP_COUNTER 确实 证明你想要什么可以完成 - 它依赖于 #include,因此仅每行一个 - 不妨使用 __LINE__。也就是说,实现太棒了,无论如何你都应该阅读它。)

您可以做的是重构您的元程序,以便计数器可以通过纯函数应用于输入数据。例如使用好的 ol' 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)
)

(向下递归列表,将子列表元素留在原处,并用递增的计数器变量替换非列表元素 - 由 8false 表示)

我假设您实际上并不想简单地在程序顶层删除 __COUNTER__ 值,因此如果您可以将需要编织 __COUNTER__ 值的代码放入将其拆分为某种类型的包装宏中序列或列表,然后您可以将列表提供给类似于示例的纯函数。

当然,能够表达此类代码的元编程库将显着__COUNTER__ 更不便携和可维护。 __COUNTER__ 受 Intel、GCC、Clang 和 MSVC 支持。 (不是每个人,例如pcc 没有它,但有人甚至使用它吗?)可以说,如果您在实际代码中演示该功能,那么标准化委员会会更有力地证明__COUNTER__ 应该成为下一个 C 标准的一部分。

【讨论】:

  • 宏扩展不允许状态更改或检查以前的状态,但预处理器是有状态的。
  • 我认为你的观点是继续使用 __COUNTER__。另外,哇,我什至不知道订单。
【解决方案2】:

你混淆了两个不同的东西:

1 - 处理#define#include 之类的东西的预处理器。它仅作为文本(即字符序列)级别起作用,并且具有很少的计算能力。它非常有限,无法实现__COUNTER__。预处理器的工作只包括宏扩展和文件替换。关键点是它发生在编译甚至开始之前。

2 - C++ 语言,尤其是模板(元)编程语言,可用于在编译阶段计算内容。它确实是图灵完整的,但正如我已经说过的,编译在预处理之后开始。

所以你所问的在标准 C 或 C++ 中是不可行的。为了解决这个问题boost 实现了自己的预处理器,它不符合标准并且具有更多的计算能力。特别是可以使用它来构建__counter__ 的类似物。

【讨论】:

  • 我完全了解 CPP 和模板元编程之间的区别。我重新命名了这个问题以消除混乱。正如我在我的问题中提到的,并在这里讨论:stackoverflow.com/a/3136798/1347646,CPP 是图灵完备的,所以它绝对应该能够做到这一点。 Boost 甚至实现了它(使用标准 CPP),因此证明它是可行的。
  • 很抱歉,您提出问题的方式太含糊了。你想生成什么?不同的字符串?不同的变量?正如我所解释的,您无法生成宏。
  • 谢谢,我已经更新了问题,希望现在更清楚。
  • 请注意,C 和 C++ 预处理器可以对常量整数表达式执行算术运算并具有条件指令。说预处理器工作只包括宏扩展和文件包含是不准确的。
  • @mic_e 我想你错过了关于“请注意,需要一个外部构建脚本来将预处理器的输出反馈到其输入,因此预处理器本身并不是图灵完备的。”听起来 Boost 做了类似的事情,所以你的选择是 (1) 使用 boost,或者 (2) 自己重新实现。
【解决方案3】:

This small header of mine 包含一个自己的 C 预处理器计数器实现(它使用稍微不同的语法)。

【讨论】:

  • 我对你的答案投了赞成票,因为它是 -1 并且没有人评论投反对票的原因。
  • @madmurphy 据我了解,用这个库实现计数器是不可能的,因为你必须将当前位置传递给宏(即来自示例:ZEN_COUNTER_NEXT(SECOND_VALUE)),所以你要知道你目前在SECOND_VALUE,而__COUNTER__的点是不知道。如果您可以展示一个没有当前计数器的库使用示例,那么我会赞成。
猜你喜欢
  • 2011-10-20
  • 2011-06-10
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 2012-06-14
  • 1970-01-01
相关资源
最近更新 更多