维测手段——使用宏替换函数调用

自从2019年2月17日断更至今,已有9个月,一方面是转行成功,从硬件可靠性测试行业转入IT,目前在从事的也是自己喜欢的C语言领域,另一方面,学海无涯,沉迷于学习无法自拔......早前制定的1000小时计划早已达成,正往2000小时方向前进,今天开始接着进行更新,进入主题,本文主要讲C开发中的维测手段,使用宏完成重复文本的替换。

 由于本人经常进行一些算法练习,所以在工作区间中,经常会使用到错误处理,即printf或者fprint来打印错误日志,如下:

int insert_circle_ll(const pstEntry_single_ll pL, const pstNode pElm, int k) {
	REG_FUNC_NAME("insert_circle_ll");
	//const char*_THIS_FUNCTION_ = "insert_circle_ll";
	enErr_val enRetVal = ERR_SUCCESS;
	if (!pL || !pElm || k < 1) {
		fprintf(stderr, "input parameter invalid!\n");
		return;
	}
    .....
}

可以看到,每次对输入参数(入参)或者使用malloc/alloc/calloc等内存分配函数进行内存分配时,安全的做法都是要进行参数合法性检测,如果量少,总是重复这些工作,如果像我在前期一样为了练打字速度的话,OK没问题,但是假以时日,当你手速已经可以跟上想法的时候,就需要考虑“我经常做练习、敲代码,总是做这样重复的事,是不是要提升提升了?”,事实上也是如此,而且这样重复的工作,很不利于在写代码时保持逻辑清晰,代码看起来也非常乱,毫无美感。正是如此,在工作和网络知识相辅相成下,我添加了这样的一段代码,极大的提高了我练习时的效率,也让手指、手腕能舒服一点了,当然,如果你像我一样喜欢敲代码,那么我建议你准备一个机械键盘......言归正传,下面是我新增的代码,只在预处理阶段被展开,不占用程序运行时间,这点,在通信IT领域相信大家都明白其价值:

#define REG_FUNC_NAME(pcName) (_THIS_FUNCTION_ = pcName)
#define DEBUG_FMT "FILEID: %d,LINE: %d,FUNC: %s"
#define DEBUG_INFO	 FIELDID,__LINE__,_THIS_FUNCTION_
#define RET_LOCAL(enRetVal) do {\
	if ((enRetVal) > ERR_ENUM_BUT || (enRetVal) < ERR_FAIL)\
		fprintf(stderr, "unknown error:" DEBUG_FMT "\n", DEBUG_INFO); \
	else if ((enRetVal) != ERR_SUCCESS)\
		fprintf(stderr, "error:" DEBUG_FMT ",%s\n", \
			DEBUG_INFO, ErrArr[(enRetVal)].info.pStr);\
	return enRetVal; \
} while (0);

我用宏RET_LOCAL(enRetVal)替换了原来的fprintf打印函数,其实就是将fprintf封装在了do{...}while(0)代码块内,同时在可维可测性上极大的提高了,定位手段增加了文件号、行号、函数名,同时将所有的错误都封装在了同一个枚举体内,当然,对变量的自注性也大大提高,每个变量只是看变量名就能知道其变量类型,这样一来,我进行参数校验的时候,只需要使用宏RET_LOCAL(),代码风格编程了如下这样子的,

int insert_circle_ll(const pstEntry_single_ll pL, const pstNode pElm, int k) {
	REG_FUNC_NAME("insert_circle_ll");
	//const char*_THIS_FUNCTION_ = "insert_circle_ll";
	enErr_val enRetVal = ERR_SUCCESS;
	if (!pL || !pElm || k < 1)	RET_LOCAL(enRetVal = ERR_INVALID_PARAMETER);
	if (CIRCLE_LIST_FULL(pL))	RET_LOCAL(enRetVal = ERR_FULL);
	pstNode pCur = pL->head;
	if (CIRCLE_LIST_EMPTY(pL)) {
		if (k > 1)	RET_LOCAL(enRetVal = ERR_INVALID_PARAMETER);
		pL->head = pElm;
		pElm->next = pElm;
	}
    ...
}

顿时清爽干净有没有?好了如果你喜欢这样的风格,那么我们就接着往下看,怎么实现这个宏?我们一一讲解:

函数名

#define REG_FUNC_NAME(pcName) (_THIS_FUNCTION_ = pcName)

首先要明确,宏的作用就是文本替换,没有其他作用,所以,_THIS_FUNCTION_ 的作用就是替换 pcName,而从其命名,就可以知道,是一个 char*类型的指针,p前缀表示指针pointer,c表示char,为了使用这个指针,首先我们要定义它,在哪里定义?有以下几点考虑:

  1. 它必须是一个全局变量;这里需要改一下,若为全局变量,当调用其他函数的时候,会因为被调函数的使用,覆盖掉调用函数的函数名,所以,这里应该声明一个局变量;
  2. 我希望它只占用一个字节的内存,即不能全局头文件中使用static关键词来存放一个指针;
  3. 我希望它不能修改所指向的地址的内容,即文件名的字符串;

 基于以上的3点要求,我分开两个文件使用它:

/**************不友好的做法,全局变量被覆盖***************/
//GLOBAL.h
extern const char *pcFuncName;

//A.c
const char *pcFuncName = NULL;

/**************使用局部变量代替***************/
#define REG_FUNC_NAME(pcName) const char*_THIS_FUNCTION_ = pcName

分别放在全局头文件和任意一个c文件中,当然,最好有专门一个c文件存放这类全局变量,毕竟这样好看又舒服。然后在每个函数中,你就可以使用宏REG_FUNC_NAME()去给函数注册函数名,用法如下:

int insert_circle_ll(const pstEntry_single_ll pL, const pstNode pElm, int k) {
	REG_FUNC_NAME("insert_circle_ll");
}

文件ID和行号

#define DEBUG_INFO	 FIELDID,__LINE__,_THIS_FUNCTION_

行号我直接使用了C所支持的宏 _ _ LINE _ _,注意前后都有下划线,而且这里为了看起来更清晰,把下划线以空格隔开,实际上是没有空格在内的;

文件ID的宏建立与函数名类型,下面直接上代码:

//GLOBAL.h
extern int iFieldId;

//A.c
#define FIELDID (FIELD_ID_START + 0)
//B.c
#define FIELDID (FIELD_ID_START + 1)
...
//Z.c
#define FIELDID (FIELD_ID_START + 25)
...

错误信息枚举

错误信息枚举把所有的错误类型都包括了进来,这里面有些小技巧,留给喜欢C代码的你发掘,话不多说,直接上代码:

//GLOBAL.h
typedef enum enFuncType {
	FUNCTION_TYPE_INVALID = -1,
	FUNCTION_TYPE_SINGLE_LINKED_LIST,
	....
	FUNCTION_TYPE_BUT,
	FUNCTION_TYPE_END
}enFuncType;
typedef enum enErr_val {
	ERR_INVALID = -1,
	ERR_FAIL,
	...
	ERR_ENUM_BUT,
	ERR_ENUM_END = 999
}enErr_val;
typedef struct ERROR_LOCAL {
	enErr_val val;
	stString info;
}stERROR_LOCAL;
extern stERROR_LOCAL ErrArr[ERR_ENUM_BUT];

//A.c
stfnCircleList fn;
stERROR_LOCAL ErrArr[ERR_ENUM_BUT] = {
	{ ERR_FAIL ,{ "fail!" , LEN_INVALID } },
	{ ERR_SUCCESS ,{ "success!" , LEN_INVALID } },
	...
	{ ERR_NO_SPECIFIC_ELEMENT ,{ "no specific element!" , LEN_INVALID } },
};

运行实例如下

维测手段——使用宏替换函数调用_第1张图片

C开发维测手段暂时就讲到这里-)

你可能感兴趣的:(数据结构,C,维测手段)