缘起
实际大型项目中的源码结构,其设计优雅、精密。阅读这样的代码,给我的感觉,就像在读一部长诗,虽然篇幅宏大,但却又不显得冗余、枯燥。不由得也想亲自尝试写一首小诗。于是就选择梳理一下最近比较感兴趣的一个部分——模板(泛化)编程。
目标
实现一种代码结构,可以完成不同类型的需求。比如,一个求和计算,不仅可以实现int变量的求和,也可以实现float变量之间的求和,类似于C++中的模板,等等。
测试环境
VC++6.0;
Widows 7 proffessional
思路
有可能实现模板的方式有两种:
1. void *的合理应用;(先不做分析)
2. 应用带参数的宏定义
在实际大型项目中,应用带参数的宏这样的方式进行接口实现是非常常见的。
宏定义在代码编译过程中,就是无条件的文本替换,不像函数那样,会有数据类型的强制要求和检查:
#include<stdio.h>
#define Sume(a, b) (a+b)
void main()
{
int a = 4, b = 5;
float c = 5.0, d = 6.0;
int result = 0;
float result_F = 0.0;
result_F = Sume( c, d);
printf("The result of two parameters is : %f\n", result_F);
result = Sume(a, b);
printf("The result of two parameters is : %d\n", result);
}
以上同样可以输出正确的结果,相较于普通函数,它的灵活性显而易见,这也就增加了C语言模板实现的可能性;当然,正因为它缺少了函数那样的类型检查等安全措施,所以使用过程中不免会有一些安全隐患,需要尤为注意。
代码实现
当然,模板不可能只局限于求和这样的简单计算,所以一种更为灵活的结构需要被广泛实现。以往参与的项目中,绝大多数是基于如下的实现:
Main.c的实现
#include<stdio.h>
/*
所谓的泛化编程,其实也少不了函数的实现体,只不过,将结构类似的实现体放在一起,便于维护管理而已
*/
/*
为了在该文件中调用
*/
#define Sum(type, a, b) Sum_##type(a, b)
/*
以下实际上编译出来是实现体,所以包含的头文件里不需要加上条件编译,否则,第二个包含将无意义,因为已经在之前包含过了,不会再被包含
*/
#define _type int
#define _func Sum_int
#include "Sum_Core.h" //事实上,这个文件里才是核心实现
#define _type float
#define _func Sum_float
#include "Sum_Core.h"
void main()
{
int a = 4, b = 5;
float c = 5.0, d = 6.0;
int result = 0;
float result_F = 0.0;
result_F = Sum(float, c, d);
printf("The result of two parameters is : %f\n", result_F);
result = Sum(int, a, b);
printf("The result of two parameters is : %d\n", result);
}
Sum_Core.h的实现
//#ifndef SUM_CFG_H
//#define SUM_CFG_H
_type _func(_type a, _type b)
{
return (a+b);
}
#undef _type
#undef _func
//#endif
按照我们正常的思路,.h中不是应该必须加上条件编译指令:
#ifdef SUM_CFG_H
#deine SUM_CFG_H
#endif
以防止同一头文件被过度调用吗?
是的,正常情况是这个样子。但是在这里的目的是为了完成不同条件下的接口实现,如果加上以上条件编译指令,则会导致一个错误:
明明已经包含了头文件,确仍然报未定义的错误。其原因就是Main.c中虽然包含了两次,但是因为有条件编译限制,防止过度包含,所以第二次实际上是没有用的。这也就导致了函数未定义的错误。
以上已经可以算得上是一个模板的雏形,但是为了更好地维护C语言模块化编程的特性,也为了便于后期维护,可以将如下部分另外加入到一个模块中,比如Sum_Cfg.h:
#define _type int
#define _func Sum_int
#include "Sum_Core.h" //事实上,这个文件里才是核心实现
#define _type float
#define _func Sum_float
#include "Sum_Core.h"
这样的话,配置、实现和调用相分离,代码结构更加清晰,后期维护起来也将更便捷一些。
总结
以上只是将自己遇到过的感兴趣的部分做了一个简单的梳理,关于模板编程仍然有许多质的探索的地方,比如,利用Void *实现模板化。