代码技巧:X-MACRO 技术,减少无谓的代码重复

前言

我是在做南京大学 ICS-PA 的时候学会的这个

写过的同学都知道这里面有非常多奥妙重重的宏定义,值得我们学习

下面,正片开始

使用场景引入

看一个例子:假如我们有若干个名字和 ID 的对应,实际情景中是 (学过计组的可以看出这个是 RISCV 中规定的 CSR 及其 id)

MTVEC: 0x305
MSTATUS: 0x300
MEPC: 0x341
MCAUSE: 0x342
SATP: 0x180

我们有多处代码,都要用到这个对应关系。比如

(1)
const char *csr_reg_names[4096] = {
	[0x305] = "MTVEC",
	[0x300] = "MSTATUS",
	[0x341] = "MEPC",
	[0x342] = "MCAUSE",
	[0x180] = "SATP"
};

(2)
const int csr_reg_id[] = {0x305, 0x300, 0x341, 0x342, 0x180};

(3)
enum { MTVEC = 0x305, MSTATUS = 0x300, MEPC = 0x341, MCAUSE = 0x342, SATP=0x180};

可以看到它们虽然长得样子不一样,但都用的是同一份 “关系”

如果我们要加入一个新的名字和id的对应,则要改三份代码

我们现在希望只写一份 “原始数据”,并且在每个地方用这个原始数据就行了,该怎么办呢?

解决:X-MACRO 技术

X-MACRO 技术做的事情就是,先写一个公用的 .h 文件(比如叫做 data.h),里面写:

MACRO(MTVEC,   0x305)
MACRO(MSTATUS, 0x300)
MACRO(MEPC,    0x341)
MACRO(MCAUSE,  0x342)
MACRO(SATP,    0x180)

然后在每次使用的时候,对 MACRO 进行定义,在数据主体的部分 #include "data.h",结束了以后 #undef MACRO

比如上面的三个例子,可以写成:

(1)
#define MACRO(NAME,ID) [ID]=#NAME,
// 这个 #NAME 代表给 NAME 周围加上引号, 变成字符串
const char *csr_reg_names[4096] = {
	#include "data.h"
};
#undef MACRO

(2)
#define MACRO(NAME,ID) ID,
const int csr_reg_id[] = {
#include "data.h"
};
#undef MACRO

(3)
#define MACRO(NAME,ID) NAME=ID,
enum {
#include "data.h"
};
#undef MACRO

这样虽然代码看起来有点不直观,但方便维护 (如果希望兼顾可读性,可以加注释,这不是问题)

后期,如果要添加更多NAME和ID,只要在 data.h 里面改一次就行了!

posted @ 2026-02-12 17:25  Flandre-Zhu  阅读(1)  评论(0)    收藏  举报