本文主要简述C语言宏展开的机制,以及如何使用宏来帮助我们实现复杂的逻辑,并记录了一些我在工作中常使用的宏定义。

以下为我使用的C语言的运行和编译环境:
System Version : Ubuntu22.04.1
Linux kernel Version : 5.19.0
GCC Version : 11.3.0
STDC Version : 201710L

摘要: C语言的编译过程,什么是预编译,宏展开的机制。

1. C语言编译过程

C语言作为一个高级编程语言,由它所编写的源代码,是需要经过编译后生成二进制可执行文件,才能被计算机上的执行者所执行。使用编译器将C语言编写的源代码翻译成二进制可执行文件的过程就称为编译过程,这个过程主要分为四个阶段:预处理、编译、汇编、链接。

1.1.预处理

编译过程的第一步预就是预处理,预处理器会将源文件进行预操作,识别源代码中的预处理指令关键字,删除所有的注释,在处理结束后会产生一个默认后缀为.i的临时文件,如若使用gcc编译器,可使用以下命令对源文件仅进行预编译操作。

1
gcc -E ./source_code.c -o source_code.i

预处理命令关键字都是以#符号开头,后面拼接关键单词组成。
常用预处理命令关键字
关键字名称用法作用
#include嵌入文件命令#include “file_path”用于将引号中所指示的文件嵌入到当前编译单元中,文件的搜寻路径和编译的设置相关
#define宏定义命令#define identifier content用于将某个内容使用其标识符代替,在预处理过程中,预处理器会将标识符替换为所表示的内容,这一过程称为宏展开
#undef取消上文宏定义命令#undef identifier用于取消编译单元下方宏定义的标识符所代表的内容,在该预处理命令的上文中,标识符还是有效的
#error强制编译停止命令#error message当预处理器遇到此命令时,会停止编译,并向编译窗口输出message的内容
#warning发出警告信息命令#warning message当预处理器遇到此命令时,会向编译窗口输出message的内容,根据编译器设置的编译类型来决定是否停止编译
#if、#else、#elif、#endif、#ifdef、#ifndef条件编译命令#if constant-expression根据常量表达式的结果来选择性进行编译包含的代码块
#line设定行号或文件名命令#line number[“filename”]改变 __LINE____FILE__ 的内容,filename是可选的参数
#pragma指示编译器命令#pragma once在该编译单元中,此文件仅会被嵌入一次

1.2. 编译

在编译阶段,编译器首先会检查源文件的语法是否符合规范,以确定代码实际的逻辑,再将编译单元翻译成汇编代码。查看汇编代码,也是定位C语言程序的问题的重要手段。在处理结束后会生成一个默认后缀为.s的临时文件,如若使用gcc编译器,可使用以下命令对源文件进行编译操作生成汇编代码文件。

1
gcc -S source_code.i -o source_code.s

1.3. 汇编

汇编阶段是把编译阶段生成的.s文件转化成目标文件。目标文件是二进制文件,不能直接查看其内容,但可以借助readelfobjdump等工具知晓其内容。在处理结束后会生成一个默认为.o的临时文件,如若使用gcc编译器,可使用以下命令生成目标文件。

1
gcc -c source_code.s -o source_code.o

1.4. 链接

在成功编译之后,就进入了链接阶段。C语言的最小编译单元就是一个后缀为.c的源代码文件,但在一个项目中,往往会将不同功能的实现划分在不同的源码文件中,在将每个源码文件编译成目标文件中,需要将其链接到一起,生成一个具有完整上下文的可执行文件。链接阶段不仅包含目标文件之间的链接,还有函数库的链接。总之,可执行文件中的所有符号在链接过程中都能找到其定义。

函数库一般分为静态库和动态库两种。静态库是指在编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不需要库文件了,其后缀一般为“.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时链接文件加载库,这样就可以节省系统的开销,动态库一般后缀名为“.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。


如若使用gcc编译器,可使用以下命令生成可执行文件。
1
gcc source_code.o -o hello_world

2.宏

2.1. 什么是宏定义

宏可以看作为一些命令的集合。它是一种预处理器命令,在预处理阶段将宏名替换为后面的内容体。
在C语言中,可以使用#define来定义宏:

1
#define macro_identifier(parament_list) content

e.g. 宏定义示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define PI                      (3.1415926)
#define CIRCLE_Perimeter(_r) (2*_r*PI)
#define CIRCLE_Area(_r) (_r*_r*PI)

int main(int argc, char **argv)
{
float radius1 = 10.0;
float radius2 = 20.0;

float Perimeter1 = CIRCLE_Perimeter(radius1);
float Perimeter2 = CIRCLE_Perimeter(radius2);
float CIRCLE_Area1 = CIRCLE_Area(radius1);
float CIRCLE_Area2 = CIRCLE_Area(radius2);
return 0;
}

上述代码中,在预处理阶段结束后,CIRCLE_PerimeterCIRCLE_Area宏会被展开,其内容如下。

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char **argv)
{
float radius1 = 10.0;
float radius2 = 20.0;

float Perimeter1 = (2*10.0*(3.1415926));
float Perimeter2 = (2*20.0*(3.1415926));
float CIRCLE_Area1 = (10.0*10.0*(3.1415926));
float CIRCLE_Area2 = (20.0*20.0*(3.1415926));
return 0;
}

2.2. 宏展开过程

在编写复杂的嵌套宏定义时,不了解宏展开的具体过程,很容易使写出的宏定义在编译时报错。以下是宏的展开过程。

  • 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  • 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  • 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果是,就重复上 述处理过程。
  • 宏展开顺序是由内而外,先展开内层宏参数,再展开外层宏函数。
  • 如果宏定义中某个形参前面有#运算符,则调用此宏定义时,不展开该形参对应的实参,而是直接把这个实参变为"字符串"
  • 如果宏定义中某个形参前面有#@运算符,则调用此宏定义时,不展开该形参对应的实参,而是直接把这个实参变为字'符'
  • 如果宏定义中##运算符的前后都是形参,则优先展开调用此宏定义,不展开该形参对应的实参,而是将##运算符前后的实参连接到一起形成一个新的符号

  1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是对于宏,不能出现递归。
  2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>

#define CAT_0 none
#define CAT_1(_a) CAT_ ## _a
#define CAT_2(_a, _b) _a ## _b
#define CAT_3(_a, _b, _c) _a ## _b ## _c
#define CAT_11(_a) CAT_ ## _a ## _a
#define CAT_22(_a, _b) _a ## _a ## _b ## _b
#if defined(WIN32)
/*< 在windows系统中, 符号 #@ 用于将token使用单引号括起来 >*/
#define TO_CHAR(_token) #@_token
#else
/*< 在其余系统中,可以对字符串使用 * 符或 [] 符取得字符 >*/
#define TO_CHAR(_token) *#_token
#endif

#define TO_CHARn(_token, n) #_token[n]
#define TO_STRING(_token) #_token
#define FOO_STRING(_token) TO_STRING(_token)

void main()
{
/*--------------------------*/
/*|-结果-----------展开次数-|*/
/*-------------------------------------------------------------------------*/
printf("%c\n", TO_CHAR(123)); /*| 1 | 1 |*/
printf("%c\n", TO_CHARn(123, 1)); /*| 2 | 1 |*/
printf("%s\n", TO_STRING(123)); /*| 123 | 1 |*/
/*-------------------------------------------------------------------------*/
printf("%s\n", TO_STRING(CAT_1(0))); /*| CAT_1(0) | 1 |*/
printf("%s\n", TO_STRING(CAT_2(1, 0))); /*| CAT_2(1, 0) | 1 |*/
printf("%s\n", FOO_STRING(CAT_1(0))); /*| none | 3 |*/
printf("%s\n", FOO_STRING(CAT_1(1))); /*| CAT_1 | 2 |*/
printf("%s\n", FOO_STRING(CAT_1(1)(0))); /*| CAT_1(0) | 2 |*/
printf("%s\n", FOO_STRING(CAT_1(2)(1, 2))); /*| 12 | 3 |*/
printf("%s\n", FOO_STRING(CAT_1(3)(1, 2, 3))); /*| 12 | 3 |*/
printf("%s\n", FOO_STRING(CAT_1(11)(2)(3, 4)));/*| 3344 | 4 |*/
/*-------------------------------------------------------------------------*/
printf("%s\n", TO_STRING(1)TO_STRING(2)); /*| 12 | 1 |*/
/*-------------------------------------------------------------------------*/
/* printf("%s\n",CAT_2(TO_STRING(1),TO_STRING(2))); */
/* 低版本gcc编译器会报错,此版本编译器不支持带有形参的宏进行拼接 */
/*-------------------------------------------------------------------------*/
/* printf("%d\n",CAT_2(CAT_, 2(2, 1))); */
/* 编译报错,在展开CAT_2后,因为最外层宏已经展开,则不会进行展开,在链接阶段会 */
/* 因为找不到 'CAT_2' 这个符号而报错 */
/*-------------------------------------------------------------------------*/
}

2.3. 常用的宏定义

以下为我在工作中经常会用到的宏定义,我这里整理了一份。

lang.h
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
/*⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⣤⣤⣤⠄⠄⠄⠄⣠⣤⣤⡄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⣀⣰⣶⣶⠄⠄⠄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⢀⣠⣶⣶⡆⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⢠⣾⣿⡿⠃⠄⠄⢀⣼⣿⡿⠛⠛⢻⣿⣿⡄⠄⠄⢰⣾⣿⣿⣿⣿⣿⠄⠄⠄⠄⢀⣼⣿⡿⠛⠛⠻⣿⣿⡆⠄⠄⠄⣾⣿⣿⣿⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⣰⣿⣿⠟⠁⠄⠄⠄⢸⣿⣿⠇⠄⠄⠈⣿⣿⣷⠄⠄⠘⠛⠋⠄⣿⣿⣿⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄⢹⣿⣿⡀⠄⠄⠛⠋⠁⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⣼⣿⣿⡃⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⢹⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠘⢿⣿⣷⣄⣀⣰⣿⣿⣿⡇⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠘⢿⣿⣷⣄⠄⠄⠄⠄⢿⣿⣿⠄⠄⠄⠄⣾⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠈⠛⠻⠿⠟⠋⢹⣿⣿⠁⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠈⢻⣿⣿⣧⡀⠄⠄⢸⣿⣿⡇⠄⠄⢠⣿⣿⡏⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢠⣿⣿⡇⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠘⢿⣿⣿⣆⠄⠄⠹⢿⣿⣷⣾⣿⡿⠏⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⢸⣷⣶⣶⣿⣿⠿⠋⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠈⠉⠉⠉⠁⠄⠄⠄⠉⠉⠉⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠄⠈⠉⠉⠉⠉⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠉⠁⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄*/
#ifndef _LANG_H_
#define _LANG_H_

#ifdef __cplusplus
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif

#endif
args.h
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⣤⣤⣤⠄⠄⠄⠄⣠⣤⣤⡄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⣀⣰⣶⣶⠄⠄⠄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⢀⣠⣶⣶⡆⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⢠⣾⣿⡿⠃⠄⠄⢀⣼⣿⡿⠛⠛⢻⣿⣿⡄⠄⠄⢰⣾⣿⣿⣿⣿⣿⠄⠄⠄⠄⢀⣼⣿⡿⠛⠛⠻⣿⣿⡆⠄⠄⠄⣾⣿⣿⣿⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⣰⣿⣿⠟⠁⠄⠄⠄⢸⣿⣿⠇⠄⠄⠈⣿⣿⣷⠄⠄⠘⠛⠋⠄⣿⣿⣿⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄⢹⣿⣿⡀⠄⠄⠛⠋⠁⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⣼⣿⣿⡃⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⢹⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠘⢿⣿⣷⣄⣀⣰⣿⣿⣿⡇⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠘⢿⣿⣷⣄⠄⠄⠄⠄⢿⣿⣿⠄⠄⠄⠄⣾⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠈⠛⠻⠿⠟⠋⢹⣿⣿⠁⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠈⢻⣿⣿⣧⡀⠄⠄⢸⣿⣿⡇⠄⠄⢠⣿⣿⡏⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢠⣿⣿⡇⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠘⢿⣿⣿⣆⠄⠄⠹⢿⣿⣷⣾⣿⡿⠏⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⢸⣷⣶⣶⣿⣿⠿⠋⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠈⠉⠉⠉⠁⠄⠄⠄⠉⠉⠉⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠄⠈⠉⠉⠉⠉⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠉⠁⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄*/
#ifndef _ARGS_H_
#define _ARGS_H_

#include <stdarg.h>

/*< 扫描 >*/
#define EXPAND(...) __VA_ARGS__

#define EVAL2(...) EXPAND(__VA_ARGS__)
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))

/*< 用 n^3 的增长速率来定义多个扫描,展开9次 >*/
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))

/*< 符号 ## 用于将两边的token进行拼接 >*/
#define _CAT6(A, B, C, D, E, F) A ## B ## C ## D ## E ## F
#define _CAT5(A, B, C, D, E) A ## B ## C ## D ## E
#define _CAT4(A, B, C, D) A ## B ## C ## D
#define _CAT3(A, B, C) A ## B ## C
#define _CAT2(A, B) A ## B
#define CAT6(A, B, C, D, E, F) _CAT6(A, B, C, D, E, F)
#define CAT5(A, B, C, D, E) _CAT5(A, B, C, D, E)
#define CAT4(A, B, C, D) _CAT4(A, B, C, D)
#define CAT3(A, B, C) _CAT3(A, B, C)
#define CAT2(A, B) _CAT2(A, B)
#define CATn(_n, ...) CAT2(_CAT,_n)(__VA_ARGS__)

#define COUNTS(_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, _31, _32, \
_33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \
_50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _, ...) _

#define PARAMS_CNT(_params...) \
COUNTS(_params, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \
48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 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)

#define ARGS_CNT(_args...) \
COUNTS(, ##_args, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \
48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 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)

#define CAT(...) CATn(ARGS_CNT(__VA_ARGS__), __VA_ARGS__)
#define MACRO_CAT(_macro, ...) CAT2(_macro, ARGS_CNT(__VA_ARGS__))(__VA_ARGS__)

/*< 符号 # 用于将token使用双引号括起来 >*/
#define STR(_token) #_token

#if defined(WIN32)
/*< 在windows系统中, 符号 #@ 用于将token使用单引号括起来 >*/
#define CHAR(_token) #@x
#else
/*< 在其余系统中,可以对字符串使用 * 符或 [] 符取得字符 >*/
#define CHAR(_token) *#_token
#endif

#define CHARn(_token, n) #_token[n]

#endif
attributed.h
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
31
/*⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⣤⣤⣤⠄⠄⠄⠄⣠⣤⣤⡄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⣀⣰⣶⣶⠄⠄⠄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⢀⣠⣶⣶⡆⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⢠⣾⣿⡿⠃⠄⠄⢀⣼⣿⡿⠛⠛⢻⣿⣿⡄⠄⠄⢰⣾⣿⣿⣿⣿⣿⠄⠄⠄⠄⢀⣼⣿⡿⠛⠛⠻⣿⣿⡆⠄⠄⠄⣾⣿⣿⣿⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⣰⣿⣿⠟⠁⠄⠄⠄⢸⣿⣿⠇⠄⠄⠈⣿⣿⣷⠄⠄⠘⠛⠋⠄⣿⣿⣿⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄⢹⣿⣿⡀⠄⠄⠛⠋⠁⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⣼⣿⣿⡃⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⢹⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠘⢿⣿⣷⣄⣀⣰⣿⣿⣿⡇⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠘⢿⣿⣷⣄⠄⠄⠄⠄⢿⣿⣿⠄⠄⠄⠄⣾⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠈⠛⠻⠿⠟⠋⢹⣿⣿⠁⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠈⢻⣿⣿⣧⡀⠄⠄⢸⣿⣿⡇⠄⠄⢠⣿⣿⡏⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢠⣿⣿⡇⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠘⢿⣿⣿⣆⠄⠄⠹⢿⣿⣷⣾⣿⡿⠏⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⢸⣷⣶⣶⣿⣿⠿⠋⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠈⠉⠉⠉⠁⠄⠄⠄⠉⠉⠉⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠄⠈⠉⠉⠉⠉⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠉⠁⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄*/
#ifndef _ATTRIBUTED_H_
#define _ATTRIBUTED_H_

#ifndef __attribute__
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
# define __attribute__(x) /* Nothing */
# endif
#endif

#define attr_aligned(x) __attribute__((aligned(x)))

#define attr_used __attribute__((__used__))

#define attr_unused __attribute__((unused))

#define attr_section(...) __attribute__((section(__VA_ARGS__)))

#endif
objectd.h
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⣤⣤⣤⠄⠄⠄⠄⣠⣤⣤⡄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⣀⣰⣶⣶⠄⠄⠄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⢀⣠⣶⣶⡆⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⢠⣾⣿⡿⠃⠄⠄⢀⣼⣿⡿⠛⠛⢻⣿⣿⡄⠄⠄⢰⣾⣿⣿⣿⣿⣿⠄⠄⠄⠄⢀⣼⣿⡿⠛⠛⠻⣿⣿⡆⠄⠄⠄⣾⣿⣿⣿⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⣰⣿⣿⠟⠁⠄⠄⠄⢸⣿⣿⠇⠄⠄⠈⣿⣿⣷⠄⠄⠘⠛⠋⠄⣿⣿⣿⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄⢹⣿⣿⡀⠄⠄⠛⠋⠁⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⣼⣿⣿⡃⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⢹⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠘⢿⣿⣷⣄⣀⣰⣿⣿⣿⡇⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠘⢿⣿⣷⣄⠄⠄⠄⠄⢿⣿⣿⠄⠄⠄⠄⣾⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠈⠛⠻⠿⠟⠋⢹⣿⣿⠁⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠈⢻⣿⣿⣧⡀⠄⠄⢸⣿⣿⡇⠄⠄⢠⣿⣿⡏⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢠⣿⣿⡇⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠘⢿⣿⣿⣆⠄⠄⠹⢿⣿⣷⣾⣿⡿⠏⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⢸⣷⣶⣶⣿⣿⠿⠋⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠈⠉⠉⠉⠁⠄⠄⠄⠉⠉⠉⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠄⠈⠉⠉⠉⠉⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠉⠁⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄*/
#ifndef _OBJECTD_H_
#define _OBJECTD_H_

#include "args.h"

/*
使用C 结构体的形式创建具有公共方法和私有方法的对象
OBJECT_METHOD 定义类的公共方法
union {iface *_public; this;} __attribute__((transparent_union)):构建一个匿名联合体的参数,
将public结构体类型转化为private结构体类型
static typeof(name) *_##name = (typeof(name)*)name;
构建一个指向该方法的指针,名称为name加上下划线,后续在COBJECT_INIT时赋值
*/
#define METHOD_NAME(_name) _ ## _name

#define METHOD_DEF(_iface, _name, _ret, _this, ...) \
static _ret METHOD_NAME(_name)(union {_iface *_public; _this;} __attribute__((transparent_union)), ##__VA_ARGS__); \
static _ret METHOD_NAME(_name)(_this, ##__VA_ARGS__)

#define METHOD_CALL(_pobj, _method, ...) _pobj->_method(_pobj, ##__VA_ARGS__)

/*
使用逗号表达式,为方法添加类似C++的默认值
开启[-Werror=unused-value]编译选项会使逗号表达式编译报错
*/
#define METHOD_DEF_VAL(_def, ...) (_def, ##__VA_ARGS__)

/*
__builtin_choose_expr是编译阶段的行为,参数1必须为常量表达式
使用METHOD_DEF_VAL1和METHOD_DEF_VAL2为方法设置至多2个默认值
*/
#define METHOD_GET_VAL1(_def, _val1, ...) METHOD_DEF_VAL(_def, ##__VA_ARGS__, _val1)
#define METHOD_GET_VAL2(_def, _val1, _val2, ...) METHOD_DEF_VAL(_def, ##__VA_ARGS__, _val2)
#define METHOD_DEF_VAL1(_def, ...) __builtin_choose_expr(ARGS_CNT(__VA_ARGS__) >= 1, METHOD_GET_VAL1(_def, ##__VA_ARGS__, _def), METHOD_GET_VAL1(_def, _def, ##__VA_ARGS__))
#define METHOD_DEF_VAL2(_def, ...) __builtin_choose_expr(ARGS_CNT(__VA_ARGS__) >= 2, METHOD_GET_VAL1(_def, ##__VA_ARGS__, _def, _def), METHOD_GET_VAL2(_def, _def, _def, ##__VA_ARGS__))

#endif
self_section.h
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⣤⣤⣤⠄⠄⠄⠄⣠⣤⣤⡄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⣀⣰⣶⣶⠄⠄⠄⠄⠄⠄⣀⣤⣶⣶⣦⣄⡀⠄⠄⠄⠄⠄⠄⢀⣠⣶⣶⡆⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⢠⣾⣿⡿⠃⠄⠄⢀⣼⣿⡿⠛⠛⢻⣿⣿⡄⠄⠄⢰⣾⣿⣿⣿⣿⣿⠄⠄⠄⠄⢀⣼⣿⡿⠛⠛⠻⣿⣿⡆⠄⠄⠄⣾⣿⣿⣿⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⣰⣿⣿⠟⠁⠄⠄⠄⢸⣿⣿⠇⠄⠄⠈⣿⣿⣷⠄⠄⠘⠛⠋⠄⣿⣿⣿⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄⢹⣿⣿⡀⠄⠄⠛⠋⠁⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⣼⣿⣿⡃⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⢹⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠘⢿⣿⣷⣄⣀⣰⣿⣿⣿⡇⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠘⢿⣿⣷⣄⠄⠄⠄⠄⢿⣿⣿⠄⠄⠄⠄⣾⣿⣿⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠈⠛⠻⠿⠟⠋⢹⣿⣿⠁⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠈⢻⣿⣿⣧⡀⠄⠄⢸⣿⣿⡇⠄⠄⢠⣿⣿⡏⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢠⣿⣿⡇⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠘⢿⣿⣿⣆⠄⠄⠹⢿⣿⣷⣾⣿⡿⠏⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⠄⠄⠄⠄⠄⢸⣷⣶⣶⣿⣿⠿⠋⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⡇⠄⠄⠄
#⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠈⠉⠉⠉⠁⠄⠄⠄⠉⠉⠉⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠉⠉⠉⠄⠄⠄⠄⠄⠈⠉⠉⠉⠉⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠉⠁⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
#⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄*/
#ifndef _SELF_SECTION_H_
#define _SELF_SECTION_H_

#include "lang.h"
#include "args.h"
#include "attributed.h"

EXTERN_C_BEGIN

#define section_boundary_upper_name(_type) CAT(_self_, _type, _start)
#define section_boundary_lower_name(_type) CAT(_self_, _type, _end)
#define section_boundary_inner_name(_type, _name) CAT(_self_, _type, _, _name)

#define section_boundary_upper(_type) \
({ extern char section_boundary_upper_name(_type)[0]; (_type *)&section_boundary_upper_name(_type); })
#define section_boundary_lower(_type) \
({ extern char section_boundary_lower_name(_type)[0]; (_type *)&section_boundary_lower_name(_type); })

#define section_location(...) attr_aligned(4) attr_unused attr_section(__VA_ARGS__)

#define section_set_boundary(_type) \
char section_boundary_upper_name(_type)[0] section_location(".self_section_" #_type "_1");\
char section_boundary_lower_name(_type)[0] section_location(".self_section_" #_type "_3")

#define section_push_obj(_type, _name, ...) \
_type section_boundary_inner_name(_type, _name) section_location(".self_section_" #_type "_2" #_name) = (__VA_ARGS__)

#define section_foreach(_type, _ptr) \
for(_ptr = section_boundary_upper(_type); _ptr != section_boundary_lower(_type); _ptr++)

EXTERN_C_END

#endif