C语言宏的妙用

C程序的编译分为预处理,编译,汇编,链接4个阶段。预处理会展开所有的宏。宏的强大在于可以构造各种语法糖,精简代码,不但使代码看起来更简洁优雅,而且没有任何执行成本(不同于函数)。举个例子,我们要创建A对象,然而在创建A对象时,可能需要创建a1,a2,a3,a4,a5,…等各种对象。只有全部a(i)对象创建成功,A才算成功。例如下面的代码(code1):

// code1.c
int createAobject (A * pA)
{
a1 = create_obj();
a2 = create_obj();
a3 = create_obj();
a4 = create_obj();
a5 = create_obj();

a = create_objA(a1, a2, a3, a4, a5);

// success
*pA = a;
return 0;
}

上面的code1代码最大的问题是没有错误检查,这是不允许的,下面加上错误检查code2:

// code2.c
int createAobject (A * pA)
{
a1 = create_obj();
if (! a1) {
// failed
return -1;
}
a2 = create_obj();
if (! a2) {
delete_obj(a1);
return -1;
}
a3 = create_obj();
if (! a3) {
delete_obj(a2);
delete_obj(a1);
return -1;
}
a4 = create_obj();
if (! a4) {
delete_obj(a3);
delete_obj(a2);
delete_obj(a1);
return -1;
}
a5 = create_obj();
if (! a5) {
delete_obj(a4);
delete_obj(a3);
delete_obj(a2);
delete_obj(a1);
return -1;
}

a = create_objA(a1, a2, a3, a4, a5);
if (! a) {
// failed
delete_obj(a5);
delete_obj(a4);
delete_obj(a3);
delete_obj(a2);
delete_obj(a1);
return -1;
}
// success
*pA = a;
return 0;
}

code2 处理的全部错误的逻辑,代码立即变得不可读了。于是再改编到code3:

// code3.c
int createAobject (A * pA)
{
a=a1=a2=a3=a4=0;
int oka5 = 0;

a1 = create_obj();
if (! a1) {
// failed
goto onerror_exit;
}
a2 = create_obj();
if (! a2) {
goto onerror_exit;
}
a3 = create_obj();
if (! a3) {
goto onerror_exit;
}
a4 = create_obj();
if (! a4) {
goto onerror_exit;
}

if (init_obj(&a5)) {
goto onerror_exit;
}
oka5 = 1;

a = create_objA(a1, a2, a3, a4, a5);
if (! a) {
goto onerror_exit;
}
// success
*pA = a;
return 0;

onerror_exit:
// failed
if (!oka5 ) {
uninit_obj(a5);
}
if (a4) {
delete_obj(a4);
}
if (a3) {
delete_obj(a3);
}
if (a2) {
delete_obj(a2);
}
if (a1) {
delete_obj(a1);
}
if (a) {
delete_obj(a);
}
return -1;
}

code3已经比code2强多了,尤其对象数目更多的时候。但是还是太啰嗦,而且依赖a(i)都是指针对象,有时候a(i)根本不是指针,就只能加个标记值oka5,例如上面的a5。于是再升级代码到code4:

// code4.c
int createAobject (A * pA)
{
int err, errlevel = 0;
// 创建对象的次序: a0,a1,a2,a3,a4,a5,a
a0 = create_obj();
check_null_object(a0, 0);

a1 = create_obj();
check_null_object(a1, 1);

a2 = create_obj();
check_null_object(a2, 2);

a3 = create_obj();
check_null_object(a3, 3);

a4 = create_obj();
check_null_object(a4, 4);

err = init_obj(&a5);
check_ret_error(err, 5);

a = create_objA(a0, a1, a2, a3, a4, a5);
check_null_object(a, 6);

// success
*pA = a;
return 0;

onerror_exit:
// 删除对象的次序: a,a5,a4,a3,a2,a1,a0
onerror_case_begin(7, destroyAobject(a));
onerror_case_level(6, delete_obj(a));
onerror_case_level(5, uninit_obj(a5));
onerror_case_level(4, delete_obj(a4));
onerror_case_level(3, delete_obj(a3));
onerror_case_level(2, delete_obj(a2));
onerror_case_level(1, delete_obj(a1));
onerror_case_level(0, delete_obj(a0));
onerror_case_end();
return -1;
}

void destroyAobject(a)
{
uninit_obj(a->a5);
delete_obj(a->a4));
delete_obj(a->a3));
delete_obj(a->a2));
delete_obj(a->a1));
delete_obj(a->a0));
delete_obj(a);
}

code4就用到了宏,从而使代码更优雅,而且确保对象创建的次序和删除的次序正好相反,宏如下:

#define check_null_object(ob, level)  if (! (ob)) goto_onerror_exit(level)

#define check_ret_error(err, level) if (err) goto_onerror_exit(level)

#define goto_onerror_exit(level) do { \
errlevel = (level); \
goto onerror_exit; \
} while(0)

#define onerror_case_begin(maxlevel, freeallobjects) do { \
if (errlevel == maxlevel) { \
(freeallobjects); \
} else { \
while (errlevel-- > 0) { \
if (errlevel < 0) break

#define onerror_case_level(level, freeobject) \
else if (errlevel == level) (freeobject)

#define onerror_case_end() \
} \
} \
} while(0)

code4展示了C语言宏对代码的友好。好好利用它!