实现一个TODO宏

实现一个TODO宏

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


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


Let’s do it

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

1
2
3
4
5
#warning sunnyxx
#error sunnyxx
#pragma message "sunnyxx"
#pragma GCC warning "sunnyxx"
#pragma GCC error "sunnyxx"

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

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

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

1
2
3
4
5
#define SOME_WARNING _Pragma("message(\"报告大王!\")")
int main() {
    SOME_WARNING // [!]报告大王!
    return 1;
}

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

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

个人认为不太可能在一个宏定义中完成这件事,需要用到辅助宏:STRINGIFY(S) 将入参转化成字符串,省去了_Pragma中全串加转义字符的困扰。
这时,一个基本功能的TODO宏就完成了,下面向其中加入额外的信息: 

1
2
3
4
5
6
7
8
// 两个已有的宏
#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__)

其中涉及到的知识: 

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

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



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

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

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


最终版本

1
2
3
4
5
6
7
8
#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

除此之外,还研究了半天如何在宏里面定义一个注释,这样就可以偷偷写//
TODO: ...
的注释,让Xcode导航栏中也出现这个TODO了:

但很可惜没有找到一个可行的方法,欢迎一起解决。
Xcode插件《XTodo》也是利用这个特性,可以尝试下。

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

同时,上面的代码在《github上》可以找到。也欢迎关注微博@我就叫Sunny怎么了一起交流。

References

http://clang.llvm.org/docs/UsersManual.html
https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html

时间: 2024-10-28 04:12:02

实现一个TODO宏的相关文章

Angularjs结合Bootstrap制作的一个TODO List_javascript技巧

看了一个关于Angularjs的视频,视频内容讲解的是如何制作一款TODO list形式的SPA(Simple Page Application,单页面应用).为了增强理解,下面写了一篇文章,用以复习巩固. 准备 Angularjs下载 说是下载,其实只要能在我们的页面中引用到Angularjs即可.可以有如下方式. CDN加速 使用国内的CDN加速服务也是可以的. 复制代码 代码如下: <script src="http://code.angularjs.org/angular-1.0.

Angularjs + Bootstrap 制作的一个TODO List

看了一个关于Angularjs的视频,视频内容讲解的是如何制作一款TODO list形式的SPA(Simple Page Application,单页面应用).为了增强理解,下面写了一篇文章,用以复习巩固. 准备 Angularjs下载 CDN加速 npm 方式 常规方式 Bootstrap下载 知识储备 MVC 架构 ng-app ng-controller ng-model 事件基础 ng-click 完整代码 mainjs todolisthtml 页面效果 代码详解 创建应用 创建控制器

宏定义的黑魔法 - 宏菜鸟起飞手册

宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在业务逻辑上,似乎对宏的使用和依赖并不多.但是使用宏定义的好处是不言自明的,在节省工作量的同时,代码可读性大大增加.如果想成为一个能写出漂亮优雅代码的开发者,宏定义绝对是必不可少的技能(虽然宏本身可能并不漂亮优雅XD).但是因为宏定义对于很多人来说,并不像业务逻辑那样是每天会接触的东西.即使是能偶尔使

关于To-Do List7件的常识

任务表只是你完成工作的一个辅助工具,别把它想得太复杂. MONEY+记者|袁园 生火.打猎.不被野兽吃掉.基本上从远古时期开始,人类的各种活动就以完成任务为导向,至今我们还能从残存的石洞壁画上了解到这些.延续到现在,这一本质依然没有改变,人们只是 转换了任务目标--比如"打猎"变成了"完成当季报表",以及需要完成的任务更多更复杂了而已. 虽然大脑一直在进化,但当需要一个人记住.完 成的任务越来越多,难免会使人产生疏漏和混乱,这 时候就需要一个非常实用的通用工具--T

Access使用宏的几点说明

在宏"条件"列不能使用 SQL 表达式. 通常情况下直接执行宏只是进行测试.可以在确保宏的设计无误之后,将宏附加到窗体.报表或控件中,以对事件做出响应,也可以创建一个执行宏的自定义菜单命令. 如果要 Microsoft Access 暂时忽略某个操作,请输入 False 为条件.暂时忽略操作有助于找出宏中的问题. 如果要快速创建一个在指定数据库对象上执行操作的宏,请从"数据库"窗口中将对象拖拽到"宏"窗口的操作行.例如,将窗体拖拽到操作行,就可以

Access宏的特点介绍

  在宏"条件"列不能使用 SQL 表达式. 通常情况下直接执行宏只是进行测试.可以在确保宏的设计无误之后,将宏附加到窗体.报表或控件中,以对事件做出响应,也可以创建一个执行宏的自定义菜单命令. 如果要 Microsoft Access 暂时忽略某个操作,请输入 False 为条件.暂时忽略操作有助于找出宏中的问题. 如果要快速创建一个在指定数据库对象上执行操作的宏,请从"数据库"窗口中将对象拖拽到"宏"窗口的操作行.例如,将窗体拖拽到操作行,就

excel表格宏按钮如何设置

  excel宏按钮设置步骤如下: 1.按钮的本质就是一个宏,所以在制作按钮之前,我们先来录制一个宏,把宏都准备好了,就可以制作按钮了.先打开一个表格如图所示,选中大标题,接下来我们在菜单栏上执行:开发工具--宏 2.设置宏名称为[大标题],保存位置为[个人宏工作簿],这样打开任意的excel就能看到这个宏,如果保存在[当前工作簿]就只能在这个excel文件中看到该宏了. 3.右键单击已经选中的单元格,在打开的右键菜单中,选择[设置单元格格式] 4.在打开的单元格格式对话框中,切换到对齐选项下,

Excel不能加载宏怎么办?

  一.因为你的宏选项里就没有可加载的宏,所以我们要先保存了宏以后才能加载的,就是说你上个文件里的宏,先保存到一个文件夹里,现在加载的时候,再找到那个文件夹里的宏,才能加载.重启一下就可以了,如果是2010版,你可以点击视图选项卡,有一个就是宏要保存到指定目录.点击另存为,类型选择加载宏,就会显示这个目录的位置. 二.点击工具,加载宏后会弹出一个窗口,问你要加载哪一类的函数,你把你需要的函数打上对勾,确定以后,你再编函数公式的时候,列表中就会有你加载的函数. 三.在excel2007以后,要保存

Excel中怎么运行宏?

  可以采用多种方法运行宏.您始终可以使用菜单命令运行宏.您也可以通过按 Ctrl 组合快捷键.单击工具栏按钮或单击对象.图形或控件上的某个区域来运行宏,具体取决于为宏指定的运行方式.另外,您可以在打开工作簿时自动运行宏.下面有个在运行宏过程中小问题,不知大家有没有遇到. 描述:有写好的宏要运行,老是提示不行,降低了安全级别 了还是提示要改一些软件的参数,是不是运行宏真的很麻烦,要怎么弄啊 解决步骤: 1.工具-宏-录制新宏,会出现对话框,设置快捷按钮(编辑区会出现一个小工具栏,先不管它) 2.