C/C++ 中的奇技淫巧:宏的妙用(一切为了偷懒)

日复一日,年复一年的“码砖”是一件极其枯燥乏味的事情。我们应该将经历放在更有趣的地方,去攻克我们前进路上的障碍。将重复无意义的“码砖”进行总结提炼,变成我们自己的轮子。

而这一想法老早就在我心中,在此抛砖引玉,希望有人有更好的建议。

在本场 Chat 中,会讲到如下内容:

  • 如何偷懒
  • 宏的特性
  • 我的偷懒方式
  • 接下来的路

适合人群: 对 C/C++ 有一定基础且有兴趣的技术人员

如何偷懒

总结出自己经常重复的工作,抽象简化它们,按照适合自己思维方式,整合成我们自己需要的。偷懒就是尽可能的少写代码,实现我们需要的效果。所以开始我的偷懒之旅吧!

宏的特性

宏的原样展开特性,对于才接触或者不怎么喜欢使用宏的人来说,宏一般用来定义常量或者类型,仅此而已,比如:

#define  PI  3.1415926#define  unsigned char  uchar

或者实现一些简单的函数功能,因为没有类型,有时候类似模板一样,可以输入使用不同类型的参数,比如:

#define  add(x,y)  (x+y)#define  sub(x,y)  (x-y)#define  mul(x,y)  (x*y)#define  dev(x,y)  (x/y)

我的偷懒方式

  • 打印输出

标准输出方式:

int delay = 5;std::cout << "delay:" << delay << std::endl;

最简单的宏实现方式就是:

#define logs(x)  std::cout << #x":" << x << std::endl;int delay = 5;logs(delay);

扩充显示文件名、函数、行号方式

#define FILE_INFO   "[" << __FILE__ << '@' << __FUNCTION__ << '#' << __LINE__  << "]"#define logs(x)     std::cout << FILE_INFO << #x":" << x << std::endl;int delay = 5;logs(delay);

对于多变量的,形如 logs(a,b,c,d) 将在另一篇 《一个极简便跨平台的 C++ 日志输出工具分享》 中详细展开讲解,希望有兴趣的小伙伴关注。

  • 指针、返回值检查

    平常判断指针,都会类似如下方式:

int ret = 3;int *ptr = &ret;if (ret == -1)    return; if (ptr == NULL)    return; 

可能带打印且有返回值,便于快速分析:

int ret = 3;int *ptr = &ret;if (ret == -1){    std::cout << "ret:" << ret << std::endl;    return; }if (ptr == NULL){    std::cout << "ptr:" << ptr << std::endl;    return false;  // 或者 -1}

可以如下实现:

#define check(var, value, ...) \    if (var == value) {\        logs(var);\        return __VA_ARGS__;\    }void test1() // 无返回值{    int ret = 3;    int *ptr = &ret;    check(ret , -1);    check(ptr, NULL);}int test2() // 有返回值{      int ret = 3;    int *ptr = &ret;    check(ret, -1, 0);    check(ptr, NULL, false);}
  • 指针释放销毁

指针内存释放,一般用自带的 delete 或者有自己的释放函数,假设如 free(),则方式如下:

xxx *ptr = new xxx;...if (ptr != NULL){    delete ptr;  // or  free(ptr);    ptr = 0;}

则可简单如下定义:

#define delptr(ptr)             if (ptr) {delete ptr; ptr = 0;}#define freeptr(fun, ptr)       if (ptr) {fun(ptr); ptr = 0;}

对于集合型指针,则可直接如下定义:

#define delptrlist(list)       for (int i = 0; i < list.size(); i++) delete list[i]; list.clear();#define delptrset(list, type)  foreach (type p, list) delptr(p); list.clear();#define delptrset11(list)      foreach (auto p, list) delptr(p); list.clear();   // 版本 >= c++11 
  • 单实例类

对于类中的一中特殊设计模式,有时候仅仅需要一个对象实例即可,可以直接简单实现如下:

#define Singleton(ClassName) \static ClassName &instance() {\     // instance() 返回的是引用,可以用 "." 运算符,比指针 “->” 少。 (^▽^)     static ClassName *m_instance = 0;\    if (m_instance == 0)\        m_instance = new ClassName;\    return *m_instance;\}

在对需要单实例的类加上如下设置即可:

class CTest{public:    Singleton(CTest)  // 添加单实例宏    ~CTest();    void xxxx();    void yyyy();private:    CTest();};// 使用方式如下:CTest::instance().xxxx();   CTest::instance().yyyy();// 更简洁的使用形式如下:#define ctest CTest::instance()ctest.xxxx();ctest.yyyy();
  • 用于数组校验

对于串口、网络数据收发时,常有一些数据计算校验计算,有的是和、有的是亦或,可以简单归纳如下:

#define calc(dat, len, opt) ({ \    uchar val = dat[0]; \    for (int i = 1; i < len; i++) \        val opt dat[i]; \    val; \})// 使用方式如下:uchar dat[30];...uchar sum = calc(dat, 30, +=);uchar xor = calc(dat, 30, ^=);

如果是集合,向量等形式,可以修改如下:

#define calc(arr, opt) ({ \    uchar val = arr[0]; \    for (int i = 1; i < arr.size(); i++) \        val opt arr[i]; \    val; \})// 使用方式如下:std::veroct  array;...uchar sum = calc(array, +=);uchar xor = calc(array, ^=);

对于非 uchar 类型数据,小伙伴们是不是可以自己很容易扩展了呢!?

接下来的路

由于受到工作方向的影响,有很多用法被局限在我们日常的工作方向中,不能很好的做出符合大家各自场景的东西。然而我觉得学习,除了学到东西,更应该获取的是思维方式。

我的砖就抛到这里,希望对你们有用。(大牛请高抬贵脚,不喜勿踩 (^▽^) )

接下来的路就请各位小伙伴们自己走了!

阅读全文: http://gitbook.cn/gitchat/activity/5f24cd1d007b9367eec77f20

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

你可能感兴趣的:(C/C++ 中的奇技淫巧:宏的妙用(一切为了偷懒))