gpt4 book ai didi

c - 轻松“反转”一组 C 预处理器宏

转载 作者:行者123 更新时间:2023-12-01 17:34:39 26 4
gpt4 key购买 nike

我有很多预处理器宏定义,如下所示:

#define FOO 1
#define BAR 2
#define BAZ 3

在实际应用中,每个定义对应一个解释器虚拟机中的一条指令。宏的编号也不是连续的,以便为将来的指令留出空间;可能有 #define FOO 41 ,那么下一个是 #define BAR 64 .

我现在正在为此虚拟机开发调试器,并且需要有效地“反转”这些前驱宏。换句话说,我需要一个接受数字并返回宏名称的函数,例如输入 2 返回 "BAR" .

当然,我可以使用 switch 创建一个函数。我:
const char* instruction_by_id(int id) {
switch (id) {
case FOO:
return "FOO";
case BAR:
return "BAR";
case BAZ:
return "BAZ";
default:
return "???";
}
}

然而,这将是一场噩梦,因为重命名、删除或添加指令也需要修改此功能。

是否有另一个宏可以用来为我创建这样的函数,还是有其他方法?如果没有,是否可以创建一个宏来执行此任务?

我在 Windows 10 上使用 gcc 6.3。

最佳答案

你有错误的方法。 阅读 SICP 如果你还没有读过它。

I have a lot of preprocessor macro definitions, like this:

#define FOO 1
#define BAR 2
#define BAZ 3

请记住 可生成 C 或 C++ 代码 ,并且很容易指导您的 build automation生成某些特定 C 文件的工具(使用 GNU make ninja 您只需添加一些规则或配方)。
例如,您可以使用一些不同的预处理器(如 GPPm4 ),或一些脚本 -e.g.在 awk PythonGuile , etc..., 或编写您自己的程序 (C, C++, Ocaml, etc...), 生成包含这些 #define 的头文件-s。另一个脚本或程序(或相同的,以不同方式调用)可以生成 instruction_by_id 的 C 代码
这样基本的 metaprogramming技术 (从更高级别但特定的东西生成一些或几个 C 文件)至少从 1980 年代开始就已使用(例如,使用 yaccRPCGEN )。 C preprocessor借助其 #include指令(因为您甚至可以在某些函数体中包含行等...)。实际上,代码是数据(和证明),数据是代码的想法甚至更早( Church-Turing thesisCurry-Howard correspondenceHalting problem )。 Gödel, Escher, Bach书非常有趣……
例如,您可以决定拥有一个文本文件 opcodes.txt (甚至一些 sqlite 数据库包含的东西......)喜欢
# ignore lines starting with an hashsign
FOO 1
BAR 2
并有两个小 awk或 Python 脚本(或两个小型 C 专用程序),一个生成 #define -s (进入 opcode-defines.h )和另一个生成 instruction_by_id 的主体(进入 opcode-instr.inc)。然后你需要调整你的 Makefile生成这些,并把 #include "opcode-defines.h"在一些全局标题内,并且有
 const char* instruction_by_id(int id) {
switch (id) {
#include "opcode-instr.inc"
default: return "???";
}
}

this will a nightmare to maintain,


这种元编程方法并非如此。您只需维护 opcodes.txt以及使用它的脚本,但您只表达一次给定的“知识元素”( FOO 与 1 的关系)一次(在 opcode.txt 的单行中)。当然,您需要记录下来(至少,在您的 Makefile 中添加注释)。
来自更高层次的元编程, declarative形式化,是一个非常强大的范式。在法国,自 1960 年代以来,J.Pitrat 开创了它(他正在写一篇有趣的 blog 今天,同时已退休)。在美国, J.MacCarthyLisp社区也。
有关有趣的演讲,请参阅 Liam Proven FOSDEM 2018 talk on The circuit less traveled
大型软件经常使用这种元编程方法。例如, GCC compiler有大约十几个 C++ 代码生成器(它们总共发出超过一百万行 C++ 行)。
看待这种方法的另一种方法是 domain-specific languages 的想法。可能是 compiled to C .如果您使用的操作系统提供 dynamic loading ,您甚至可以编写一个发出 C 代码的程序, fork 一个进程以将其编译成某个插件,然后加载该插件(在 POSIX 或 Linux 上,使用 dlopen )。有趣的是,计算机现在速度足够快,可以在交互式应用程序中启用这种方法(在某种 REPL 中):您可以发出几千行的 C 文件,将其编译成一些 .so共享对象文件和 dlopen那,在几分之一秒内。您还可以使用 JIT 编译库,如 GCCJIT或 LLVM 在运行时生成代码。您可以在程序中嵌入解释器(如 LuaGuile )。
顺便说一句,元编程方法是基本 compilation 的原因之一。大多数开发人员(而不仅仅是编译器行业的人)都应该知道技术;另一个原因是 parsing问题很常见。所以阅读 Dragon Book .
注意 Greenspun's tenth rule .这不仅仅是一个玩笑,实际上是一个关于大型软件的深刻真理。

关于c - 轻松“反转”一组 C 预处理器宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49829673/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com