使用宏灵活的控制代码
2012-04-14 19:22
141 查看
这个系列记录了前一个项目中的一些心得,《接下来一段时间的工作》列了一个目录。不过具体记录时,没有按照目录的顺序。
第一篇记一下关于宏的一些使用。
有一些(特别是纯C++)程序猿,认为宏在代码中的作用不大,尤其是使用const XXX代替宏的号召使得很多刚开始学习C或者转为C++的人放弃了使用宏。其实宏有很多用处,尤其是纯C或者嵌入式编程中,比如:
1.求一个结构体中某类型的偏移量、字节数等
2.位运算等
3.求地址、取高低位等
...
不过这些都是处理一些技术细节上的技巧,我想说的是在大型项目中如何使用宏技巧来控制代码,中间有一些方法日常写代码可会用到。
1.错综复杂的头文件包含时,用#ifdef...组合来防止头文件重复包含(略过...)
2.跨平台
不同平台下不同编译器对C/C++的标准执行的不一致,因此如果考虑到后面有跨平台项目的移植问题,那么在项目代码的基础库中最好使用宏将一些基础关键字定义
3.使用宏定义具有本项目特色的变量类型(typedef也可以)
example:
#deine int MyPrjInt
4.dll导出
大型项目多包含n个dll,各种功能块由不同的程序猿分别实现,他们向外输出dll供别人使用,所以就存在dll导出函数和导出类的问题。使用宏可以很方便的控制这些逻辑。
example:
4.统一定义一些Debug时的检查宏
为了防止出现一些错误导致程序crash,程序猿都会在代码中加入一些用于检查的代码,比如判断指针是否为空等,但是每个程序猿写的检查代码不一样,那么就会出现千奇百怪的检查函数和宏,不方便统一管理,也不方便自动化脚本在crash后去定位和统计问题。因此有必要提供一些统一的检查代码。
example:
5.将一组C接口封装为类的成员方法
对于一些C语言类型的dll导出接口,使用起来是很危险的,比如一个功能A,他本身实现时是在dll中使用面向对象的方式,但是向外提供了一组C类型的接口并要求所有使用者通过全局变量同享这个功能。那么对于N个使用者来说,获取这个全局变量并使用功能A就需要协商,90%的情况下可能会使用单件的方式,那么也就需要定义单件类并重新封装这组接口。改代码是可以实现的,但是通过宏还控制,也许更简单。
example:
6.压缩代码(生成代码)
有一些公共的操作,几乎在所有的方法中都会被执行,或者几乎在所有的方法被使用之前都需要被执行。那么首先想到的就是将这个操作封装为一个函数,在前面提到的那些位置被调用。这个当然可以,但是函数的代价就是,执行时会伴随着压栈出栈等操作,如果操作数量到达一定的量级,也是会对性能造成影响的。
另一种方法就是把这个公共的操作代码在每一个地方复制粘贴一遍......这个虽然不会存在调用时的性能问题,但是你懂的...
最好的方式就是使用宏函数来实现,由于宏是工作在编译阶段,那么好处就是:
一、编译时宏被实际的代码替换了,其实还是相当于公共的代码在每一个需要的地方直接出现了,不存在调用函数时的压栈问题了;
二、编译成二进制文件后,重复出现又怎么样呢?方正源代码只会该宏定义处有我需要的逻辑,需要对逻辑修改时也只用改那一点。
这里和第4点提到的检查函数是有一些相似之处的。
7.动态生成一些变量等
这点主要用的是#和##两个操作符
#用于在其后的变量两边添加引号,那么就可以使用这个性质来生成字符串
example:
example:
宏的使用大致就这些,没有涉及到纯C中使用很精细的宏的介绍。
下一篇介绍C++模版的一些高级使用技巧。
第一篇记一下关于宏的一些使用。
有一些(特别是纯C++)程序猿,认为宏在代码中的作用不大,尤其是使用const XXX代替宏的号召使得很多刚开始学习C或者转为C++的人放弃了使用宏。其实宏有很多用处,尤其是纯C或者嵌入式编程中,比如:
1.求一个结构体中某类型的偏移量、字节数等
2.位运算等
3.求地址、取高低位等
...
不过这些都是处理一些技术细节上的技巧,我想说的是在大型项目中如何使用宏技巧来控制代码,中间有一些方法日常写代码可会用到。
1.错综复杂的头文件包含时,用#ifdef...组合来防止头文件重复包含(略过...)
2.跨平台
不同平台下不同编译器对C/C++的标准执行的不一致,因此如果考虑到后面有跨平台项目的移植问题,那么在项目代码的基础库中最好使用宏将一些基础关键字定义
3.使用宏定义具有本项目特色的变量类型(typedef也可以)
example:
#deine int MyPrjInt
4.dll导出
大型项目多包含n个dll,各种功能块由不同的程序猿分别实现,他们向外输出dll供别人使用,所以就存在dll导出函数和导出类的问题。使用宏可以很方便的控制这些逻辑。
example:
//1.平台管理 #if defined _WINDOWS//windows平台 #define MY_PRJ_DLL_IMPORT __declspec(dllimport)//导入dll关键字 #define MY_PRJ_DLL_EXPORT __declspec(dllexport)//导出dll关键字 #define MY_PRJ_CALL __cdecl//函数调用方式 #define MY_PRJ_CALLBACK __cdecl//回调函数调用方式 #else //...可定义其他平台 #else #endif //2.dll中还是exe中? #ifdef MY_PRJ_EXPORTS #define MY_PRJ_API(Func_Return_Type) MY_PRJ_DLL_EXPORT retype MY_PRJ_CALL #else #define MY_PRJ_API(Func_Return_Type) MY_PRJ_DLL_IMPORT retype MY_PRJ_CALL #endif //3.使用时 #define _WINDOWS //#define MY_PRJ_EXPORTS MY_PRJ_API(int) Func(); //编译时转为---> //__declspec(dllexport) int __cdecl Func();
4.统一定义一些Debug时的检查宏
为了防止出现一些错误导致程序crash,程序猿都会在代码中加入一些用于检查的代码,比如判断指针是否为空等,但是每个程序猿写的检查代码不一样,那么就会出现千奇百怪的检查函数和宏,不方便统一管理,也不方便自动化脚本在crash后去定位和统计问题。因此有必要提供一些统一的检查代码。
example:
//定义检查宏 #define CHECK_FUNC(x) / if ((retCode = (x)) != MY_OK) / { / fprintf(stderr, "My_Func[%d] error./n", retCode); / } //定义函数 RET_CODE My_Func(); //使用检查宏并调用函数 CHECK_FUNC(My_Func());
5.将一组C接口封装为类的成员方法
对于一些C语言类型的dll导出接口,使用起来是很危险的,比如一个功能A,他本身实现时是在dll中使用面向对象的方式,但是向外提供了一组C类型的接口并要求所有使用者通过全局变量同享这个功能。那么对于N个使用者来说,获取这个全局变量并使用功能A就需要协商,90%的情况下可能会使用单件的方式,那么也就需要定义单件类并重新封装这组接口。改代码是可以实现的,但是通过宏还控制,也许更简单。
example:
//原来提供的一组C接口 MY_PRJ_API(int) Func1(); MY_PRJ_API(void) Func2(); MY_PRJ_API(double) Func3(); MY_PRJ_API(bool) Func4(); MY_PRJ_API(char*) Func5(); //定义用于封装的宏 #define BEGIN_SINGLETON(SingletonClassName)\ class SingletonClassName\ {\ public:\ static HINSTANCE m_hInstance; #define END_SINGLETON() }; //使用宏将其变为单件模式接口 BEGIN_SINGLETON(MY_SINGLETON_INTERFACE) (int) Func1(); (void) Func2(); (double) Func3(); (bool) Func4(); (char*) Func5(); END_SINGLETON()
6.压缩代码(生成代码)
有一些公共的操作,几乎在所有的方法中都会被执行,或者几乎在所有的方法被使用之前都需要被执行。那么首先想到的就是将这个操作封装为一个函数,在前面提到的那些位置被调用。这个当然可以,但是函数的代价就是,执行时会伴随着压栈出栈等操作,如果操作数量到达一定的量级,也是会对性能造成影响的。
另一种方法就是把这个公共的操作代码在每一个地方复制粘贴一遍......这个虽然不会存在调用时的性能问题,但是你懂的...
最好的方式就是使用宏函数来实现,由于宏是工作在编译阶段,那么好处就是:
一、编译时宏被实际的代码替换了,其实还是相当于公共的代码在每一个需要的地方直接出现了,不存在调用函数时的压栈问题了;
二、编译成二进制文件后,重复出现又怎么样呢?方正源代码只会该宏定义处有我需要的逻辑,需要对逻辑修改时也只用改那一点。
这里和第4点提到的检查函数是有一些相似之处的。
7.动态生成一些变量等
这点主要用的是#和##两个操作符
#用于在其后的变量两边添加引号,那么就可以使用这个性质来生成字符串
example:
#define GET_STRING( str ) #str char *p = GET_STRING(123456); //-----> //char *p = "123456";##用于拼接,可以用这个性质来生成变量类型
example:
//用年级、班级_编号这样一个结构来描述一个学生, //当需要生成一个变量来班级,要求使用下面的结构 struct Student { // }; Student Grade3_Class2_43; Student Grade6_Class1_5; Student Grade2_Class5_21; Student Grade4_Class1_17; //使用宏 #define GET_STUDENT(gradeNum,classNum,stuNum) Grade##gradeNum##_Class##_classNum##_##stuNum Student GET_STUDENT(3,3,25); //---> //Student Grade3_Class3_25;
宏的使用大致就这些,没有涉及到纯C中使用很精细的宏的介绍。
下一篇介绍C++模版的一些高级使用技巧。
相关文章推荐
- 版本控制(下)——使用Git将代码托管到github
- 使用XML和java代码混合控制UI
- 使用git和github 进行代码的版本控制
- 关于CDHtmlDialog的控制。MFC Web控件的灵活使用
- 在Drupal中灵活使用区块代码
- iOS开发-新版Xcode在Appdelegate中通过代码控制跳转,不使用系统默认跳转到默认ViewController
- 原创: 自己收集整理的 DELPHI 中控制与使用HMTL帮助文件的单元代码。
- 使用Java代码控制ADF table列进行QBE方式的过滤
- PHP5中使用DOM控制XML实现代码
- 使用定时器判断确保某个标签有值才执行方法, 控制js代码执行先后顺序
- 编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程
- quarta使用代码控制定时任务的启停
- Unity3D-ScrollView中使用Layout布局后用代码如何控制ScrollView移动到指定位置
- 使用Java代码控制CPU占用率
- 使用脚本控制网页Table的显示隐藏(全代码)_AX
- 使用XML布局文件和java代码混合控制UI界面
- 使用XML布局文件和java代码共同控制UI界面做一个简易图片浏览器
- 使用javascript灵活控制DIV的位置
- 使用xml和java代码混合控制UI界面
- 使用XML文件和Java代码控制UI界面