最近我在修改我前同事写的代码,我现在是习惯模块化编程,因为这样可以降低降低编写难度,降低逻辑耦合性,也就是将分别定义模块的源文件和头文件,模块的源文件和头文件中对模块的细节用c语言进行描述,定义模块相关的宏定义、数据类型,全局变量、操纵函数。
例如:有两个模块:
模块A:stm32单片机的flash,于是我在模块A中定义了描述flash的各种宏值、以及操纵函数,因为操作那个flash的隔离函数我在函数定义的时候习惯带返回值,但是这个返回值为宏定义中错误码中的一种,而非返回数据,如果需要返回数据,我在参数列表中使用了指针变量中的输出型参数,用于存储返回值。
模块B:命令行cmd,也就是一个uint8_t类型的数组中的数据,这些数据由前缀+数据长度+操纵码+有效数据+后缀组成的,有固定的格式,因此我定义了一对cmd.h和cmd.c用于描述命令行。定义了一些列的宏定义,以及操作那个cmd的各种函数。其中操纵cmd的各类函数的返回值也是以"错误码"的方式记录函数内部根据参数值的不同运行的不同路线(出口)。
遇到问题:
北京:模块B(cmd)中有些操纵函数需要调用模块A(flash)的某些函数,调用这些函数经常使用的方式就是利用函数调用的返回值和模块A(flash)自身的错误码(宏定义)做比较,如下所示:
模块B(cmd)中:
cpp
uint8_t cmd_calvalue_program(uint8_t *cmd_data, uint8_t cmd_cnt)
{
if(flash_program_halfword(CMD_CALVALUE_ABSSTARTADDR, temp) != FLASH_ERROR_OK)
{
return CMD_ERROR_PROGRAMFLASH
}
...
}
现在,遇到的问题是:当我模块B中某个函数的返回值是根据模块A函数调用返回值和模块A中自身的错误码比较的结果来决定的。因此模块B返回值错误码的含义往往与模块A的错误码含义相似甚至相同,那么模块B中函数直接使用模块A中的返回值是否正确合理,如果不合理,怎么处理呢?
答案:不合理,不能使用模块A中的宏定义错误码作为模块B函数返回值。模块B应该定义自己的错误码的宏定义,就算是与模块A宏定义的含义是相同的,也要定义模块B自己的错误码宏定义。需要注意的是,模块A和模块B定义含义相同宏定义的时候,宏名不能相同,需要自带各自模块的前后缀,用于区分。
试想一下,如果模块B中直接使用了模块A的错误码宏定义有哪些危害?首先危害最大的就是破坏了模块A和模块B之间独立性,使得你中有我,我中有你,代码逻辑变得紊乱异常,破坏了模块化编程的目的(各模块干个模块的工作,模块之间联系或者耦合性低)。其次,会造成严重的错误,试想一下如果模块B函数中调用了模块A的错误码x(1)作为代码正确运行的结果,因为我们知道宏定义在预处理器的预处理阶段会进行一次问题替代处理,假设模块B中已经定义了一个错误码y(0)作为代码运行的结果0,这样代码逻辑就严重错误了。模块B运行正确实际返回了1,但是我们是希望0,这就会造成严重的逻辑紊乱,而且很难查出来。
因此正确的做法就是:模块A和模块B如果有其他模块也一样的,都定义各自一套错误码宏定义,各个模块之间如果错误码宏定义含义相同,也需要带上各个模块含义的前缀或者后缀用于区分,各个模块中的函数的返回值,使用各自的错误码宏定义,千万不能混用(会造成严重的逻辑紊乱和逻辑错误)。
再思考一个问题:各个模块定义错误码宏定义的宏值,如果遇到宏含义相同的情况,是否有必要写成一致的呢?比方说宏值都是0表示成功,1表示失败?
答:完全没有必要做到宏含义相同时宏值也想通,也不容易做到。
因为代码是对模块细节的描述,当我们写模块A的错误码的时候可能想到那个错误就先写那个错误,对应的宏值可能就是0 1 2 3 ...依次下去,甚至写模块A的函数的时候才想起需要再补充某个错误码,于是就回到错误码定义的地方,再继续累加。写模块B的错误码的情况与模块A相似,也是相似的情景,就算遇到了需要定义相同宏含义的情况,也很难保证宏值是一样的。因此,各个模块错误码定义中宏含义相同对应的宏值不同是常态,相同才是巧合。因此我们写各自模块错误码的时候不用考虑其他模块的宏值是什么。只需要定义好自己模块的错误码宏定义即可,增加对应前后缀以区分。
错误码映射:
其实,前面已经讲解的模块B的返回值是有模块A中函数调用返回值与模块A比较的结果决定的,这种情况,往往会发生两个模块含义相同的情况,就有了模块A的错误码宏定义--->决定了模块B的错误码宏定义,这就叫错误码映射。