问题

写代码时,遇到需要定义一个字符串数组和一个函数指针数组,字符串数组的值和函数指针名称一一对应。按照一般初始化方式,需要重复输入很多内容,并且因为字符串数组和函数指针数组可能会有增减,因此一般方式就很繁琐,想着用宏定义是否可解决。

问题示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//函数
typedef void (*FUNC)(void);
void func_a(){}
void func_b(){}
void func_c(){}

//需要定义字符串数组
char* strList[3] = {"my_a","my_b","my_c"};

//需要定义函数指针数组
FUNC funcList[3] = {func_a, func_b, func_c};

//一般按照以上方式定义,但如果数量很多,并且可能编码过程中有增减,就会显得繁琐
//想要通过宏定义方式定义两个数组
#define MCRO_INIT(...)
#define init(...) MCRO_INIT(__VA_ARGS__) //使用宏定义

init(a,b,c);

宏参数数量

定义数组需要设置数组的长度,可以设置一个较大值,保证之后即便添加元素,也不会超出长度。但这显然不太优雅,对于空间有点浪费,作为嵌入式程序员,是不能容忍这种浪费行为的。因此在使用宏时,能确定传入的参数数量是最好的。

以下宏正可以解决此问题:

1
2
3
4
5
#define ARG_T(t)  t  //解决VC编译错误
#define ARG_N(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,N,...) N //限制了可变参数计算的范围[1,32]
#define ARG_N_HELPER(...) ARG_T(ARG_N(__VA_ARGS__)) //辅助宏
#define COUNT_ARG(...) ARG_N_HELPER(__VA_ARGS__,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) //返回可变参数个数

需要注意的是,该宏计算参数的数量是有上限的,如果预期参数数量超出,需要在宏中继续添加。虽然不是一劳永逸,但是目前最好的方法了。

参数扩展

如果仅仅只是传递参数数列,没有必要对参数进行扩展,如:

1
#define LOG(fmt,...) printf(fmt "\n",__VA_ARGS__)

以上__VA_ARGS__替换...中的参数传递给printf,不需要扩展。

但对我的问题来说就不够了,需要为传入的参数添加前缀。另外,传入参数不能是字符串,因为函数名称不是字符,宏办不到把字符串的引号去掉,因此传入的参数只能是a,b,c,而不是"a","b","c"。将参数转为字符串,加#就可以了。

那么如何扩展呢,感谢文章最上方两个链接解答了这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

#define HMConcat(A, B) _HMConcat(A, B)
#define _HMConcat(A, B) A##B

#define HMMacroArgCheck(...) \
_HMMacroArgCheck(__VA_ARGS__, \
N, N, N, N, N, N, N, N, N, N, \
N, N, N, N, N, N, N, N, N, N, \
N, N, N, N, N, N, N, N, N, N,1)

#define _HMMacroArgCheck(\
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
TARGET, ...) TARGET

#define HMForeach(MACRO, ...) HMConcat(_HMForeach, HMMacroArgCheck(__VA_ARGS__)) (MACRO, __VA_ARGS__)
#define _HMForeach() HMForeach
#define _HMForeach1(MACRO, A) MACRO(A)
#define _HMForeachN(MACRO, A, ...) MACRO(A)HMDefer(_HMForeach)() (MACRO, __VA_ARGS__)

#define HMEmpty()
#define HMDefer(ID) ID HMEmpty()

#define HMExpand(...) _HMExpand1(_HMExpand1(_HMExpand1(__VA_ARGS__)))
#define _HMExpand1(...) _HMExpand2(_HMExpand2(_HMExpand2(__VA_ARGS__)))
#define _HMExpand2(...) _HMExpand3(_HMExpand3(_HMExpand3(__VA_ARGS__)))
#define _HMExpand3(...) __VA_ARGS__

以上宏实现了参数的扩展,具体使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//字符串列表
#define MY_STR(x) "my_"#x,
#define STR_LIST(...) HMExpand(HMForeach(MY_STR,__VA_ARGS__))

//函数列表
#define MY_FUNC(x) func_##x,
#define FUNC_LIST(...) HMExpand(HMForeach(MY_FUNC,__VA_ARGS__))

//函数声明
#define MY_DECLARE(x) void func_##x(void);
#define FUNC_DECLARE(...) HMExpand(HMForeach(MY_DECLARE,__VA_ARGS__))

#define init(...) \
FUNC_DECLARE(__VA_ARGS__)\
char* strList[COUNT_ARG(__VA_ARGS__)]={STR_LIST(__VA_ARGS__)};\
FUNC funcList[COUNT_ARG(__VA_ARGS__)]={FUNC_LIST(__VA_ARGS__)}



init(a,b,c);
//之后想要添加一个字符串`"my_d"`和函数`func_d`到数组,只需要在参数列表添加即可
init(a,b,c,d);

参考文章