前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC 实现一个TODO宏

OC 实现一个TODO宏

作者头像
用户1941540
发布2018-05-11 13:33:43
1.3K0
发布2018-05-11 13:33:43
举报
文章被收录于专栏:ShaoYL

实现一个TODO宏

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

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

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

Let’s do it

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

代码语言:javascript
复制
#warning sunnyxx
#error sunnyxx
#pragma message "sunnyxx"
#pragma GCC warning "sunnyxx"
#pragma GCC error "sunnyxx"
但我们知道,带 ==#== 的预处理指令是无法被 ==#define== 的。好在C99提供了一个 ==_Pragma== 运算符可以把部分 ==#pragma== 指令字符串化:
代码语言:javascript
复制
#pragma message "sunnyxx"
// 等价于
_Pragma("message \"sunnyxx\"") // 需要注意双引号的转义
// 或
_Pragma("message(\"sunnyxx\")") // 需要注意双引号的转义
1.利用这个特性,我们就可以将warning定义成宏
代码语言:javascript
复制
#define SOME_WARNING _Pragma("message(\"报告大王!\")")

int main() {
    SOME_WARNING // [!]报告大王!
    return 1;
}
2.接下来,我们让这个宏能够接受入参,并显示到warning中去,这里会面临宏的基本用法的考验。
代码语言:javascript
复制
#define STRINGIFY(S) #S
#define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG)))
3.个人认为不太可能在一个宏定义中完成这件事,需要用到辅助宏:==STRINGIFY(S)== 将入参转化成字符串,省去了==_Pragma==中全串加转义字符的困扰。 这时,一个基本功能的TODO宏就完成了,下面向其中加入额外的信息:
代码语言:javascript
复制
// 两个已有的宏
#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,把宏定义成前面加@的形式
代码语言:javascript
复制
#define KEYWORDIFY try {} @catch (...) {}
5.将最终的宏定义前面加上上面的宏后,使用时就可以加@前缀了(空的try-catch会被编译器优化,所以没啥性能损耗)

最终版本

代码语言:javascript
复制
#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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-11-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实现一个TODO宏
    • 下面一步步来实现这个宏。
      • 但我们知道,带 ==#== 的预处理指令是无法被 ==#define== 的。好在C99提供了一个 ==_Pragma== 运算符可以把部分 ==#pragma== 指令字符串化:
      • 1.利用这个特性,我们就可以将warning定义成宏
      • 2.接下来,我们让这个宏能够接受入参,并显示到warning中去,这里会面临宏的基本用法的考验。
      • 3.个人认为不太可能在一个宏定义中完成这件事,需要用到辅助宏:==STRINGIFY(S)== 将入参转化成字符串,省去了==_Pragma==中全串加转义字符的困扰。 这时,一个基本功能的TODO宏就完成了,下面向其中加入额外的信息:
      • 4.其实到这步已经OK了,为了让这个宏更加抢眼,还可以借鉴RAC,把宏定义成前面加@的形式:
      • 5.将最终的宏定义前面加上上面的宏后,使用时就可以加@前缀了(空的try-catch会被编译器优化,所以没啥性能损耗)
    • 最终版本
      • What’s more
      • Xcode插件《XTodo》也是利用这个特性,可以尝试下。
      • References
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档