在Linux内核、嵌入式代码等传统的C代码里,会有一些难以识别的宏定义。我记得在eCos, UBoot, FFmpeg有一些比较BT的宏定义,很难读懂。对于C++程序员来说,最好将这种难读的宏定义转成inline函数或模板函数。本章对这些较难的重定义进行汇总。
#define FPOS_TO_VAR(fpos, typed, var) (var) = (typed)((fpos).__pos)
#define VAR_TO_FPOS(fpos, var) (fpos).__pos = (var)
此句在faac的代码里可以见到,其特殊之处是以类型名作为参数,而且是用宏定义一行赋值语句。像如下的调用:
uint64_t ret;
FPOS_TO_VAR(fpos, uint64_t, ret);
相当于
uint64_t ret;
ret = (uint64_t) (fpos.__pos);
一般可以对宏定义作换行显示,换行后会更清晰易懂一些:
#define FPOS_TO_VAR(fpos, typed, var) \
(var) = (typed)((fpos).__pos)
这种用法似乎在uCosII中用到,记不清了。举例来说,我们知道一个struct(类型为T)中有一个变量为v,那么由v的地址来得到T的指针呢?
#define S_OFFSET(T,v) (&((T*)0)->v) struct ABC { int a; char b; int c; }; int main() { unsigned int offset = (unsigned int) S_OFFSET(ABC, b); return 0; }
T* p = (T*) 0; // 把0地址强转为一个T* void* offset = &p->v; // 由于变量p的地址为0,所以成员变量v的偏移就是其地址得到上述偏移之后怎么用呢?在函数传递的过程中,只需要一个v的指针,就可以反推出其所在的结构的地址。而offset一般不方便指定为一个固定值,因为不同环境下的padding可能不一致,也不方便扩展。
void* p = ...; T* t = (T*) (p - offset);
这也是一种比较特殊的用法,在eCos3中经常用到。用于替换一个完整名字(类型名、函数、变量名) 的一部分。以下再种写法等效,请仔细体会:
my_type_t var1;
my_type_t var2;
和
struct my_type_t { int a; char b; }; #define MY_TYPE(T, name) \ T var##name MY_TYPE(my_type_t, 1); MY_TYPE(my_type_t, 2); int main() { unsigned int offset = (unsigned int) S_OFFSET(my_type_t, b); var1.a = 10; }
#define MYSTR(value) #value int main() { char* cc = MYSTR(hello); }相当于
char* cc = "hello";
【待续】