实现一个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

目录
相关文章
|
Oracle 安全 关系型数据库
实战篇:Linux 安装 Oracle 11GR2 数据库保姆级教程(一)
实战篇:Linux 安装 Oracle 11GR2 数据库保姆级教程(一)
实战篇:Linux 安装 Oracle 11GR2 数据库保姆级教程(一)
|
人工智能 自然语言处理 数据挖掘
云上玩转Qwen3系列之三:PAI-LangStudio x Hologres构建ChatBI数据分析Agent应用
PAI-LangStudio 和 Qwen3 构建基于 MCP 协议的 Hologres ChatBI 智能 Agent 应用,通过将 Agent、MCP Server 等技术和阿里最新的推理模型 Qwen3 编排在一个应用流中,为大模型提供了 MCP+OLAP 的智能数据分析能力,使用自然语言即可实现 OLAP 数据分析的查询效果,减少了幻觉。开发者可以基于该模板进行灵活扩展和二次开发,以满足特定场景的需求。
|
9月前
|
存储 物联网 PyTorch
不用换显卡!大模型微调显存优化实操指南(附代码+效果对比)
不用换显卡!本文详解三大显存优化技巧:梯度检查点、混合精度训练、动态批量调整,附PyTorch实操代码与效果对比。16G显卡成功微调Llama 2 7B,显存占用直降38.5%,精度几乎无损,学生党、个人开发者也能轻松上手。
不用换显卡!大模型微调显存优化实操指南(附代码+效果对比)
|
机器学习/深度学习 传感器 搜索推荐
《洞察因果本质:解锁智能体大模型精准预测的底层逻辑》
因果表征学习作为新兴领域,致力于挖掘数据背后的因果结构,突破传统基于相关性的表征学习局限,为智能体大模型提供更精准的预测能力。通过揭示数据生成机制、打破相关性局限和整合多源信息,它在工业故障预测、农业产量优化、电商推荐等领域展现巨大潜力。尽管面临数据挖掘与框架融合等挑战,但其未来前景广阔,有望推动医疗、金融、智慧城市等多领域实现更深层次的理解与决策优化。
461 11
|
人工智能 BI 语音技术
AR眼镜+AI大模型:颠覆工业设备验收流程的智能革命
本方案结合AR眼镜与AI视觉大模型,打造高效、精准、可追溯的设备验收流程。通过第一视角记录、智能识别、结构化数据生成与智能报表功能,提升验收效率与质量,助力企业实现智能化管理。
快手自动抢红包辅助插件,快手抢红包福袋脚本全自动,智能抢包软件按键版
这是一套快手自动抢红包插件源码,通过模拟点击实现自动化操作。例如准备100个快手账号配合此插件挂机抢红包
|
存储 Kubernetes 测试技术
企业级LLM推理部署新范式:基于ACK的DeepSeek蒸馏模型生产环境落地指南
企业级LLM推理部署新范式:基于ACK的DeepSeek蒸馏模型生产环境落地指南
1009 12
|
存储 消息中间件 运维
单体应用与微服务的优缺点
单体应用(monolith application)就是将应用程序的所有功能都打包成一个独立的单元,可以是 JAR、WAR、EAR 或其它归档格式。
904 0
|
存储 JavaScript 前端开发
基于SpringBoot的医护人员排班系统(代码+数据库+文档)
基于SpringBoot的医护人员排班系统(代码+数据库+文档)
|
机器学习/深度学习 数据采集 人工智能

热门文章

最新文章