C99 预处理器图灵完整吗?

2023-11-25

发现后增强预处理器的能力我发现自己在想:C99 预处理器图灵完整吗?

如果没有的话,缺少什么才没有资格呢?


宏不会直接递归扩展,但我们可以通过一些方法来解决这个问题。

在预处理器中执行递归的最简单方法是使用延迟表达式。延迟表达式是需要更多扫描才能完全扩展的表达式:

#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__

#define A() 123
A() // Expands to 123
DEFER(A)() // Expands to A () because it requires one more scan to fully expand
EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan

为什么这很重要?当宏被扫描和扩展时,它会创建一个禁用上下文。此禁用上下文将导致引用当前扩展宏的标记被涂成蓝色。因此,一旦它被涂成蓝色,宏将不再扩展。这就是宏不递归扩展的原因。然而,禁用上下文仅在一次扫描期间存在,因此通过推迟扩展,我们可以防止宏被涂成蓝色。我们只需要对表达式应用更多扫描。我们可以使用这个来做到这一点EVAL macro:

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

现在如果我们想实现一个REPEAT使用递归的宏,首先我们需要一些增量和减量运算符来处理状态:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define INC(x) PRIMITIVE_CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 9

#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8

接下来我们需要更多的宏来执行逻辑:

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)

#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,

#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0

#define BOOL(x) COMPL(NOT(x))

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define IF(c) IIF(BOOL(c))

#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)

现在有了所有这些宏,我们可以编写一个递归函数REPEAT宏。我们使用一个REPEAT_INDIRECT宏递归地引用自身。这可以防止宏被涂成蓝色,因为它将在不同的扫描上扩展(并使用不同的禁用上下文)。我们用OBSTRUCT这里,这将推迟两次扩张。这是必要的,因为有条件WHEN已应用一次扫描。

#define REPEAT(count, macro, ...) \
    WHEN(count) \
    ( \
        OBSTRUCT(REPEAT_INDIRECT) () \
        ( \
            DEC(count), macro, __VA_ARGS__ \
        ) \
        OBSTRUCT(macro) \
        ( \
            DEC(count), __VA_ARGS__ \
        ) \
    )
#define REPEAT_INDIRECT() REPEAT

//An example of using this macro
#define M(i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7

现在,由于计数器的限制,该示例仅限于 10 次重复。就像计算机中的重复计数器会受到有限内存的限制一样。可以将多个重复计数器组合在一起来解决此限制,就像在计算机中一样。此外,我们可以定义一个FOREVER macro:

#define FOREVER() \
    ? \
    DEFER(FOREVER_INDIRECT) () ()
#define FOREVER_INDIRECT() FOREVER
// Outputs question marks forever
EVAL(FOREVER())

这将尝试输出?永远,但最终会停止,因为不再应用任何扫描。现在的问题是,如果我们给它无限次扫描,这个算法能否完成?这被称为停机问题,图灵完备性对于证明停机问题的不可判定性是必要的。正如您所看到的,预处理器可以充当图灵完整语言,但它不受计算机有限内存的限制,而是受到所应用扫描的有限数量的限制。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C99 预处理器图灵完整吗? 的相关文章

  • 可以在 VS2008 中的预处理器指令块(例如 #ifndef ... #endif)中启用智能感知吗?

    在 C 库中工作时 我注意到在 ifndef CLIENT DLL endif 等指令块内我没有被授予任何智能感知 这显然是由于 CLIENT DLL 已被定义的事实 我意识到我可以通过简单地注释掉指令来解决这个问题 无论指令评估如何 是否
  • 如何暂时禁用 C/C++ 中的宏扩展?

    由于某种原因 我需要暂时禁用头文件和 undef MACRONAME将使代码编译 但它将在现有宏下进行 有没有办法禁用它 我应该提到 您并不真正知道宏的值 并且我正在寻找交叉编译器解决方案 至少应该在 GCC 和 MSVC 中工作 在 MS
  • 奇怪的宏定义问题

    我想在编译时根据另一个宏的值定义一个宏 但是这段代码没有按预期执行 include
  • 使用 C 中的宏初始化未知大小的二维数组

    我正在开发一个小型宏项目 该项目要求我将二维数组文字传递给我的宏之一 如下所示 myMacro 0 1 2 2 1 0 不必将数组文字的大小传递给宏 有没有办法将其扩展为以下内容 int 2 3 0 1 2 2 1 0 或等效的东西 任何保
  • 实现编译时机制检查字符串的唯一性

    定义我的问题的最简单方法是我正在尝试实现一种机制来检查是否已使用相同的字符串 或一对 数字 字符串 我希望使用 C 预处理器以智能方式实现此机制 我还希望这种机制在调试模式下出现冲突或运行时错误时 通过检查断言 给我编译错误 我们不希望开发
  • 从源代码编译 Ruby 1.8.7 时出错:math.c:37: 错误:标记“(”之前缺少二元运算符

    这真的很奇怪 josh josh wget ftp ftp ruby lang org pub ruby 1 8 ruby 1 8 7 tar bz2 josh josh tar xvjf ruby 1 8 7 tar bz2 josh j
  • 你能在 C 中#define 注释吗?

    我正在尝试做一个调试系统 但它似乎不起作用 我想要完成的是这样的 ifndef DEBUG define printd else define printd printf endif 有没有办法做到这一点 我有很多调试消息 但我不喜欢这样做
  • 如何检索 C99 可变参数宏的最后一个参数?

    Visual Studio 失败的 static assert 错误消息完全由错误代码和 static assert 的第二个参数组成 没有任何其他消息表明这是静态断言失败 我想做一个宏来解决这个问题 例如 作为第一次尝试 define S
  • GCC/CLANG 与 MSVC 的预处理器之间有何差异?

    以下预处理器宏 通常的嫌疑人 测试空参数列表并计算参数数量 在 gcc clang 上运行时没有警告 但在 Microsoft VisualC 上失败 IS EMPTY returns nothing if the parameter li
  • 无与伦比的括号宏怪异

    根据 C99 规则预处理以下 3 行的正确输出是什么 define y x x define x a y a x 1 x 2 顺便说一句 linux 下的 cpp 会产生错误消息 但我不明白为什么答案不简单 1 2 假设 cpp 是正确的而
  • C 预处理器字符串化怪异

    我正在定义一个宏 该宏的计算结果为常量字符串 保存文件名和行号 用于记录目的 它工作正常 但我只是不明白为什么需要 2 个额外的宏 STRINGIFY and TOSTRING 当直觉简单地表明 FILE LINE include
  • 列出 C 常量/宏

    有没有办法使GNU C 预处理器 cpp 或其他一些工具 列出给定点上的所有可用宏及其值C file 我正在寻找特定于系统的宏 同时移植一个已经精通 UNIX 的程序并加载一堆稀疏的 UNIX 系统文件 只是想知道是否有比寻找定义更简单的方
  • 用宏包装函数(无需重命名)C

    我有兴趣通过包装现有函数调用来添加一些额外的逻辑without重命名它们 仅供测试 我发现的现有解决方案依赖于将函数包装在不同名称的宏中 这可能意味着更改大量代码 有什么建议么 请注意 我知道LD PRELOAD 但我有兴趣使用宏来检查传递
  • 在 C 或 C++ 中使用逗号作为宏名称

    我想做这样的事情 define define MAX 10 000 000 undef 有什么技巧可以做到吗 编辑 我知道 C 14 中的数字分隔符 我正在寻找一种技巧来对不兼容的编译器执行相同的操作 EDIT2 请考虑Variadic M
  • #define, #ifdef #undef #endif

    我有以下代码 define PROC ADD void main void while 1 ifdef PROC ADD Do this code here then undefined it to run the code in the
  • 结合 C++ 和 C - #ifdef __cplusplus 如何工作?

    我正在从事一个有很多遗产的项目C代码 我们已经开始使用 C 进行编写 目的是最终转换遗留代码 我有点困惑如何C和C 交互 我明白 通过包装C代码与extern C C 编译器不会破坏C代码的名称 但我不完全确定如何实现它 所以 在每个的顶部
  • 为什么乘法比除法便宜?

    我最近编写了一个 Vector 3 类 并将我的 normalize 函数提交给朋友审阅 他说这很好 但我应该尽可能乘以倒数 因为在 CPU 时间上 乘法比除法便宜 我的问题很简单 这是为什么 从硬件可以更轻松地实现的基本运算的角度来考虑
  • 包含防护的推荐命名约定是什么?

    包含守卫通常如何命名 我经常看到这样的情况 ifndef FOO H define FOO H endif 但是 我认为这不是很直观 如果看不到文件名 很难分辨出什么内容FOO H它的用途和它的名字所指的是什么 什么被认为是最佳实践 我个人
  • 如何在 Clang 中检测 libstdc++ 版本?

    我想用 Clang 编写一个 可移植 C 库 可移植 意味着我 在 C 预处理器中 检测编译环境中可用的 C 功能 并使用这些功能或提供我的解决方法 这与 Boost 库所做的类似 然而 某些功能的存在并不取决于语言 而是取决于标准库的实现
  • 如何在 C 预处理器中可靠地检测 Mac OS X、iOS、Linux、Windows? [复制]

    这个问题在这里已经有答案了 如果有一些跨平台 C C 代码需要在 Mac OS X iOS Linux Windows 上编译 我如何在预处理器过程中可靠地检测到它们 大多数编译器都使用预定义的宏 您可以找到列表here http sour

随机推荐

  • 获取给定 System.Type 的结构的大小

    给定一个结构体MyStruct 我可以使用以下方法获取该结构实例的大小sizeof MyStruct 在不安全的代码中 但是 我想获取给定结构的大小Type结构体的对象 即sizeof typeof MyStruct 有Marshal Si
  • MYSQL - 无法连接到“localhost”上的 MYSQL 服务器 (10061)

    我已经在我的电脑上安装了 wamp 服务器 它没有互联网或内联网连接 Windows XP 操作系统 但是当我访问MYSQL时会弹出这个错误 你能告诉我如何解决这个错误吗 非常感谢 无法连接到 localhost 上的 MYSQL 服务器
  • 构建应用程序时 Xcode 错误:第 7 行:/resources-to-copy-Project.txt:权限被拒绝

    当我尝试在 Xcode 中构建 cordova 应用程序时 出现以下错误 Users User PhpstormProjects project project app platforms ios Pods Target Support F
  • 需要归档CLLocation数据

    我有一个数组CLLocation我想要存档的数据 应该NSUserDefaults系统可以用吗 否则 如何最好地归档CLLocation data 要正确存储 CLLocation 而不会丢失信息 请使用 NSKeyedArchiver 如
  • 如何从同一模块中的类名字符串获取类对象?

    我有课 class Foo def some method pass 还有另一个班级在同一个模块中 class Bar def some other method class name Foo Can I access the class
  • 未解决的外部符号错误仅发生在 64 位模式中,而不是在 32 位构建中

    我有一个 VC 代码 使用 VS2008 构建 它使用了一些静态库 在编译时静态链接的 lib 文件 为了便于理解 我们将我的 EXE 代码称为 AAA EXE 并将 lib 文件称为 A lib b lib 等 AAA EXE代码和静态库
  • php 发送带有附件的电子邮件

    我似乎找不到我编写的这个应该发送带有附件的电子邮件的 php 函数的问题 我已经为此挣扎了很长一段时间 function myMail to subject mail msg filename contentType random hash
  • 由于用空格扩展变量而导致的 Grep 错误

    我有一个名为 physics 1b sh 在 bash 中 如果我尝试 x physics 1b grep string x sh grep 抱怨 grep physics 1b No such file or directory 然而 当
  • 使用 Ruby on Rails 安排发送电子邮件任务的最佳方式是什么?

    我想安排一项日常任务 每天早上 7 点 我希望发送一封电子邮件 无需人工干预 我正在研究 RoR 框架 我想知道最好的方法是什么 我听说过 BackgrounDRB OpenWFEru 调度程序或基于 Cron 的东西 但我是新手 不明白哪
  • Oracle SQL 在包含数据时将列类型从 number 更改为 varchar2

    我在 Oracle 11g 中有一个表 包含数据 我需要使用 Oracle SQLPlus 执行以下操作 目标 更改列的类型TEST1在表中UDA1 from number to varchar2 建议的方法 备份表 将列设置为空 改变数据
  • CKEditor 插入 HTML

    我有数据库中的数据 在我的 js 文件中 我想更改 CKEditor 文本编辑器的值 我的值是原始 html 我希望将此原始值写入空的 CKEditor 文本编辑器 我尝试了这些 但总是出现未定义的函数错误 CKEDITOR instanc
  • java编译中出现未检查或不安全操作错误? [复制]

    这个问题在这里已经有答案了 我正在完成学校的实验作业 并在编译时收到此错误 程序运行良好 有点想修复导致错误的原因 程序代码和完整错误如下 一如既往的感谢 错误 注意 F Java Lab 8 Lab8 java 使用未经检查或不安全的操作
  • Android 中 Activity 的服务回调

    我有一个正在运行的后台服务和一个与该服务交互的客户端 当客户端请求某些操作时 服务会执行该操作并将结果发送回活动 客户端 我知道如何调用活动中的服务方法 并使用回调我们可以实现我想要做的事情 但我无法理解Api demos remotese
  • 为什么Android模拟器的编号是5554

    这可能是一个完全开箱即用的问题 我只是想知道为什么Android模拟器的编号是5554 5556这样的 Android 模拟器使用网络与 Android SDK 工具进行通信 即使此通信仅限于本地主机 这允许安装应用程序 调试等 当您启动模
  • 以编程方式检索 Google Sheets 单元格边框样式

    有可能set the 范围边框样式和颜色 现在的问题是 我们怎样才能get以编程方式设置边框样式 我正在寻找类似的东西 var ss SpreadsheetApp getActiveSpreadsheet var sheet ss getS
  • Node.js 在断开连接事件时不发送套接字

    当有人连接到节点服务器时 我会保留一个包含所有套接字的数组 这样我就可以在需要时向每个人广播消息 或者循环用户来计算在线用户的数量等 所有这些工作正常 但是当触发断开连接事件时 我在参数中没有收到套接字 还有另一种方法可以知道哪个套接字刚刚
  • CSS 相当于 Photoshop 的 Justify-All

    我想采用 h2 元素并将其文本跨越其 div 的宽度 text align justify 仅当文本的宽度大于其容器的宽度时才展开文本 有点像 Photoshop 的 justify left CSS h2 text align justi
  • Swing,如何正确更新UI

    在 Swing 上进行一些操作后更新 UI 的正确方法是什么 例如 单击按钮后 调用的方法可能几乎是即时的 也可能需要几秒钟的时间 事实上 所有应用程序逻辑都是通过 Web 服务远程完成的 因此等待应用程序响应一段时间是正常的 我的按钮事件
  • opencv中的“InputArray”和“Mat”是否相同?

    例如 在文档中有 void cv absdiff InputArray src1 InputArray src2 OutputArray dst 是不是等同于 void cv absdiff Mat src1 Mat src2 Mat ds
  • C99 预处理器图灵完整吗?

    发现后增强预处理器的能力我发现自己在想 C99 预处理器图灵完整吗 如果没有的话 缺少什么才没有资格呢 宏不会直接递归扩展 但我们可以通过一些方法来解决这个问题 在预处理器中执行递归的最简单方法是使用延迟表达式 延迟表达式是需要更多扫描才能