实现一个TODO宏

转载http://blog.sunnyxx.com/2015/03/01/todo-macro/

实现一个能产生warning的TODO宏,用于在代码里做备忘,效果:



下面一步步来实现这个宏。

Let’s do it

手动让编译器报警(报错)可以用以下几个方法:

#warning sunnyxx
#error sunnyxx
#pragma message "sunnyxx"
#pragma GCC warning "sunnyxx"
#pragma GCC error "sunnyxx"

但我们知道,带 # 的预处理指令是无法被 #define 的。好在C99提供了一个 _Pragma 运算符可以把部分 #pragma 指令字符串化:

#pragma message "sunnyxx"
// 等价于
_Pragma("message \"sunnyxx\"") // 需要注意双引号的转义
// 或
_Pragma("message(\"sunnyxx\")") // 需要注意双引号的转义

1.利用这个特性,我们就可以将warning定义成宏

#define SOME_WARNING _Pragma("message(\"报告大王!\")")

int main() {
SOME_WARNING // [!]报告大王!
return 1;
}

2.接下来,我们让这个宏能够接受入参,并显示到warning中去,这里会面临宏的基本用法的考验。

#define STRINGIFY(S) #S
#define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG)))

3.个人认为不太可能在一个宏定义中完成这件事,需要用到辅助宏:STRINGIFY(S) 将入参转化成字符串,省去了_Pragma中全串加转义字符的困扰。

这时,一个基本功能的TODO宏就完成了,下面向其中加入额外的信息:

// 两个已有的宏
#define STRINGIFY(S) #S
#define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG)))
// 延迟1次展开的宏
#define DEFER_STRINGIFY(S) STRINGIFY(S)
// 下面的宏在第一行用`\`折行
#define FORMATTED_MESSAGE(MSG) "[TODO-" DEFER_STRINGIFY(__COUNTER__) "] " MSG " \n" \
DEFER_STRINGIFY(__FILE__) " line " DEFER_STRINGIFY(__LINE__)

其中涉及到的知识:

  1. 两个常量字符串可以拼接成一个整串 “123””456” => “123456”
  2. 使用到3个预定义宏,__COUNTER__宏展开次数的计数器,全局唯一;__FILE__当前文件完整目录字符串;__LINE__在当前文件第几行
  3. 在字符串中预定义宏应延时展开,如果将上面的DEFER_STRINGIFY换成STRINGIFY的话,如__LINE__就不能被正确展开成行数,而是成了一个常量字符串"LINE"
  4. 为了美化,warning message中可以使用\n换行

于是,使用FORMATTED_MESSAGE(MSG)宏就可以将带文件路径、序号、行数等信息加入到最终的warning中。

4.其实到这步已经OK了,为了让这个宏更加抢眼,还可以借鉴RAC,把宏定义成前面加@的形式:

#define KEYWORDIFY try {} @catch (...) {}

5.将最终的宏定义前面加上上面的宏后,使用时就可以加@前缀了(空的try-catch会被编译器优化,所以没啥性能损耗)

最终版本

#define STRINGIFY(S) #S
#define DEFER_STRINGIFY(S) STRINGIFY(S)
#define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG)))
#define FORMATTED_MESSAGE(MSG) "[TODO-" DEFER_STRINGIFY(__COUNTER__) "] " MSG " \n" \
DEFER_STRINGIFY(__FILE__) " line " DEFER_STRINGIFY(__LINE__)
#define KEYWORDIFY try {} @catch (...) {}
// 最终使用下面的宏
#define TODO(MSG) KEYWORDIFY PRAGMA_MESSAGE(FORMATTED_MESSAGE(MSG))

What’s more

Xcode插件《XTodo》也是利用这个特性,可以尝试下。

如果需要一个产生error的宏,将这里替换成这样就好了:_Pragma(STRINGIFY(GCC error(MSG)))

References

http://clang.llvm.org/docs/UsersManual.html

https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html

OC 实现一个TODO宏的更多相关文章

  1. 代码备忘, TODO宏实现

    代码备忘, TODO宏实现 我们平时在开发过程中, 往往并非憋足气一股脑敲完所有代码.每一个模块, 每一个函数的实现总有个先后顺序. 又或者哪个部分须要做调整, 改动- 所以, 我们须要有一个东西, ...

  2. 用 Swift 开发一个 TODO 应用

    背景 相信不少 iOS 程序员对于 Swift 依旧持以观望的态度,一来是这小家伙刚出来没几天,本身还处于完善的阶段:二来是学习的成本较高,看完官方文档怎么也要个几天的时间:三来是反正最近几年很难在工 ...

  3. oc是一个全动态语言,oc的一切都是基于runtime实现的!

    oc是一个全动态语言,oc的一切都是基于runtime实现的! 从以下三方面来理解runtime吧! 1. 传统的面向过程的语言开发,例如c语言.实现c语言编译器很简单,只要按照语法规则实现一个LAL ...

  4. 用react + redux + router写一个todo

    概述 最近学习redux,打算用redux + router写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 注意: 我只实现了Footer组件的router,其它组件的实现方法是类 ...

  5. 用react+redux写一个todo

    概述 最近学习redux,打算用redux写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 代码 代码请见我的github 组织架构如下图:

  6. react写一个todo

    概述 最近学习redux,打算先复习一下react,所以用react写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 代码 代码请见我的github 组织架构如下图:

  7. Confluence 6 创建一个用户宏

    如果你想创建自定义的宏的话,用户宏能够帮你完成这个任务.这个可以在你系统中应用特定的操作,比如说应用自定义格式等. 用户用是在 Confluence 创建和和管理的,你需要有一定的编码基础才可以. 你 ...

  8. Directx11教程(11) 增加一个debug宏

    原文:Directx11教程(11) 增加一个debug宏       现在我们在common.h中增加一个debug的宏,在每个d3d11函数后调用,如果d3d函数出错,它能够给出程序中错误的代码行 ...

  9. 写一个TODO App学习Flutter本地存储工具Moor

    写一个TODO App学习Flutter本地存储工具Moor Flutter的数据库存储, 官方文档: https://flutter.dev/docs/cookbook/persistence/sq ...

随机推荐

  1. jdbc 操作步骤

    package org.db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLExcept ...

  2. HDU1074 Doing Homework —— 状压DP

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1074 Doing Homework Time Limit: 2000/1000 MS (J ...

  3. POJ1417 True Liars —— 并查集 + DP

    题目链接:http://poj.org/problem?id=1417 True Liars Time Limit: 1000MS   Memory Limit: 10000K Total Submi ...

  4. git 一次删除所有删除的文件

    /*********************************************************************************** * git 一次删除所有删除的 ...

  5. bzoj 1086 王室联邦 —— 思路题

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1086 一眼看去很是不会,于是看看TJ... https://blog.csdn.net/ly ...

  6. bzoj4806 炮——DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4806 看到这题首先会想到状压什么乱七八糟的,然而很难做: 其实,因为求的是方案数,所以并不需 ...

  7. Python基础第十天

    一.内容

  8. hdu3652(含有13且能被13整除的数)数位DP基础

    B-number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  9. Asp.net Mvc 数据库上下文初始化器

    在Asp.net Mvc 和Entity FrameWork程序中,如果数据库不存在,EF默认的行为是新建一个数据库.如果模型类与已有的数据库不匹配的时候,会抛出一个异常. 通过指定数据库上下文对象初 ...

  10. 3198: [Sdoi2013]spring【容斥原理+hash】

    容斥是ans= 至少k位置相等对数C(k,k)-至少k+1位置相等对数C(k+1,k)+至少k+2位置相等对数*C(k+2,k) -- 然后对数的话2^6枚举状态然后用hash表统计即可 至于为什么要 ...