0

0

C++如何实现简易的断言宏支持日志?(自定义assert)

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-02-26 15:11:10

|

704人浏览过

|

来源于php中文网

原创

标准 assert 不能直接打日志,因其失败时调用 abort() 且不输出文件名、行号、表达式原文等关键调试信息;需封装 log_assert 宏,用 do-while 和逗号表达式确保线程安全、不重复求值,并支持自定义消息与 release 级别配置。

c++如何实现简易的断言宏支持日志?(自定义assert)

为什么标准 assert 不能直接打日志

标准 assert 在失败时调用 abort(),不输出上下文,也不走你的日志系统。它甚至不保留文件名、行号、表达式原文——这些恰恰是调试时最需要的信息。

真正能用的日志断言,得自己封装一层,把触发条件、位置、可读描述都塞进日志管道里。

  • assert 展开后是宏,不是函数,没法加日志调用逻辑
  • 宏展开发生在预处理阶段,__FILE____LINE__ 是可靠的,但 __func__ 在某些旧编译器(如 GCC 4.7 之前)可能不可用
  • 别试图在宏里调用带副作用的函数(比如 log_error(...) 多次求值),表达式必须只执行一次

怎么写一个线程安全、不重复求值的 LOG_ASSERT

核心是用逗号表达式 + do {...} while(0) 结构,确保语义完整且可放在任何语句位置。关键点:先求值,再判断,失败才打日志。

#define LOG_ASSERT(expr) \
    do { \
        auto _expr_val = (expr); \
        if (!_expr_val) { \
            log_error("ASSERT failed: {} at {}:{} ({})", #expr, __FILE__, __LINE__, __func__); \
            std::abort(); \
        } \
    } while(0)
  • _expr_valauto 避免类型硬编码,兼容 intbool、指针等
  • #expr 把原始表达式转成字符串,比只打 "expr" 有用得多
  • 如果日志函数不是 noexcept,且你启用了异常(比如 log_error 内部抛异常),std::abort() 前可能已崩溃——这时优先保证 abort 不被绕过
  • Windows 下若用 OutputDebugString 替代 log_error,注意字符串需为 UTF-16,得转换

如何让 LOG_ASSERT 支持自定义消息

标准 assert 没消息字段,但实际开发中“空指针”和“超时未响应”需要不同提示。C++20 的 static_assert 支持字符串字面量,但运行时 assert 不行——只能靠宏重载。

Warp
Warp

新一代的终端工具(内置AI命令搜索)

下载

立即学习C++免费学习笔记(深入)”;

用 GCC/Clang 的变参宏扩展(C99 标准,C++11 起广泛支持):

#define LOG_ASSERT(...) LOG_ASSERT_IMPL(__VA_ARGS__)
#define LOG_ASSERT_IMPL(expr, ...) \
    do { \
        auto _expr_val = (expr); \
        if (!_expr_val) { \
            log_error("ASSERT failed: {} at {}:{} ({}) — {}", #expr, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
            std::abort(); \
        } \
    } while(0)
  • 调用形式:LOG_ASSERT(ptr != nullptr, "ptr is null after init")
  • ##__VA_ARGS__ 是 GCC 扩展,用于处理零参数情况(避免末尾逗号),MSVC 需用 __VA_OPT__(C++20)或单独写两个宏
  • 如果日志库不支持格式化(比如只接受 const char*),就别拼接字符串,改用多参数日志接口,或提前 snprintf 到栈缓冲区

Release 构建下要不要关掉 LOG_ASSERT

要,但别简单用 #ifdef NDEBUG。标准 assertNDEBUG 下消失,但你的 LOG_ASSERT 如果也这样,会导致测试环境通过、线上崩溃——因为某些“断言”其实在做必要校验(比如指针非空、数组索引合法)。

  • 区分两类行为:DEBUG_ASSERT(仅调试)和 ENSURE(生产环境也要检查,失败则记录并返回错误码)
  • 不要让 LOG_ASSERT 在 Release 下静默失效;至少保留求值+日志,去掉 abort(),或换成可配置的回调(如 on_assert_fail
  • 如果项目用 CMake,可通过 target_compile_definitions(mylib PRIVATE LOG_ASSERT_LEVEL=1) 控制粒度,0=关闭,1=日志不 abort,2=日志+abort

最容易被忽略的是:宏里的表达式在 Release 下是否仍会被编译器优化掉?答案是否定的——只要宏展开后代码存在,即使没分支逻辑,(ptr->data) 这种解引用仍会触发空指针访问。所以,真要关,得用 if constexpr(C++17)或模板特化,而不是单纯删宏。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

248

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

906

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

831

2023.08.22

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

103

2023.09.25

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

553

2023.09.20

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

638

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

218

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1560

2023.10.24

Golang 实际项目案例:从需求到上线
Golang 实际项目案例:从需求到上线

《Golang 实际项目案例:从需求到上线》以真实业务场景为主线,完整覆盖需求分析、架构设计、模块拆分、编码实现、性能优化与部署上线全过程,强调工程规范与实践决策,帮助开发者打通从技术实现到系统交付的关键路径,提升独立完成 Go 项目的综合能力。

1

2026.02.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10.2万人学习

C 教程
C 教程

共75课时 | 5万人学习

C++教程
C++教程

共115课时 | 19.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号