C语言宏的妙用

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语言宏对代码的友好。好好利用它!

你可能感兴趣的:(c)