我这人做项目,最受不了的就是代码里面一堆重复啰嗦的玩意儿。尤其是在做底层文件操作的时候,简直是噩梦。
我被重复代码搞崩溃了
你写个打开文件的操作,就一个fopen,后面必须跟一堆判断:是不是空指针?是不是打开失败了?失败了咋办?得打日志?还得跳转去清理资源?每次遇到文件读写、内存分配或者网络连接,那段“检查-日志-清理”的代码段就得手动写一遍,少说五六行。搞得我头大,代码一塌糊涂,别人看了也难受。

所以我就开始琢磨,能不能用宏把这事儿给彻底解决了?
我第一个想到的就是去抄别人的作业,看那些开源项目,尤其是搞内核或者高性能计算的,他们怎么处理错误。我先是瞄上了那些标准日志宏,比如那种只负责打印LOG_ERROR("发生错误在 %s:%d", __FILE__, __LINE__)的。这玩意儿只负责吼一嗓子,但对程序流程控制一点用都没有,该跳出该清理,还得自己写if (result != 0) goto cleanup;。

后来我注意到了大家口中的那种类似fs的宏,通常这种宏在特定的环境里(比如某些文件系统层或者特定库里)很流行。它们厉害的地方在于,把检查、日志和跳转给封装在一起了。比如它可能长得像:FS_CHECK(error_code, handle),如果出错了,它内部就帮你把错误信息塞进去,然后让你能快速收尾。
我实践和对比了一圈
我直接动手拆解了两种思路:
第一种:标准库的思路。 依赖函数返回值,调用者自己判断,自己调用日志函数,自己决定是返回还是
goto。优点是灵活,但缺点就是太他妈的重复了。第二种:类似
fs宏的思路。 宏里面包含了日志和跳转到统一的清理标签(cleanup)。这大大简化了调用方的代码,一行宏搞定所有。但问题来了,每个项目的清理流程和日志格式都不一样,直接搬运别人的宏,改起来比自己写还费劲。
有一次,我为了测试一个宏的性能,直接把一个文件读写函数,用标准方法写了三百多行,然后用宏封装的方法写了不到一百行。性能上几乎没区别,但是代码清晰度简直是天壤之别。
我拍板决定,必须自己写一个。
我的最终选择:量身定做
我的做法是,我从fs宏那里偷师了“自动跳转到清理块”这个核心思想,但把日志和错误码的结构进行了大改造。我给自己定义了一个叫DO_OR_RETURN(CALL, ERR_VAL)的宏。这个宏专门用来包裹那些有返回值的函数调用:
它干了三件事:
1. 执行调用: CALL。
2. 检查结果: 如果结果失败(比如返回-1或NULL)。
3. 快速收尾: 立刻打印出“哪个文件、哪一行”出了问题,并且执行goto cleanup。
这样一来,所有的错误处理都集中到了函数末尾的那个cleanup:标签下面。我只需要在这里统一释放资源(关闭文件、释放内存),代码流程就变得超级干净和稳健。
实践证明,自己动手量身定做的宏,比任何通用的“万能”宏都好用。 像fs这种宏通常服务于特定的大型框架,它有它预设的清理逻辑。而我的业务逻辑千变万化,只有把检查和流程控制权抓在手里,才能确保在任何错误发生时,我的资源都能被正确释放。要问怎么选?答案就是:先看别人怎么干,然后撸起袖子,搞个最适合自己项目的定制版。
