使用__FILE__和__LINE__定位错误

2023-05-16

[前言:使用__FILE__和__LINE__来定位错误已经屡见不鲜,然而其中一些道理又有几个人仔细探究过。本文参考了Curtis Krauskopf的一篇名为Using __FILE__ and __LINE__ to Report Errors 的文章,希望达到解惑之效。]

问题:当运行时错误产生时,我怎样才能得到包含C++文件名和行号的字符串信息?
回答:在C++中的__FILE__预编译指示器包含了被编译的文件名,而__LINE__则包含了源代码的行号。__FILE__和__LINE__的前后都包含了两个下划线,让我们仔细看看__FILE__所包含的每个字符:

_ _ F I L E _ _

下面展示了在控制台程序中如果显示文件名和代码行号。

#include  < stdio.h >

int  main( int  ,  char ** )
{
     printf(
" This fake error is in %s on line %d/n " ,         __FILE__, __LINE__);
     
return   0 ;
}


输出结果:

This fake error is in c:/temp/test.cpp on line 5

让我们更上一层楼

我想通过一个通用函数error()来报告错误,以使当某个错误发生时我能设置断点以及隔离错误处理(例如,在屏幕上打印错误信息或者写入日志)。因此,函数的原型应该是这样的吧:

void  error( const   char   * file,  const  unsigned  long  line, const   char   * msg); 


调用方式如下:

error(__FILE__, __LINE__,  " my error message " );


预处理魔法

这里有三个问题需要解决:

  1. __FILE__和__LINE__在每次调用error时作为参数传入。
  2. __FILE和__LINE__前后的下划线很容易被遗忘,从而导致编译错误。
  3. __LINE__是一个整数,这无疑增加了error函数的复杂度。我绝不想直接使用整型的__LINE__,而通常都是将转换为字符串打印到屏幕或写入日志文件。

__FILE__和__LINE__应该被自动处理,而非每次作为参数传递给error,这样会让error的使用者感觉更爽些,它的形式可能是下面这样:

error(AT,  " my error message " );

 
我希望上面的宏AT展开为:"c:/temp/test.cpp:5"。而新的error函数则变成:

void  error( const   char   * location,  const   char   * msg);

 

因为Borland C++ Builder编译器能够自动合并相邻的字符串,因此我把AT写成下面这样:

#define  AT __FILE__ ":" __LINE__ 


然而它却罢工了,因为__LINE__被扩展成了整数而非字符串,所以宏展开后变成:

"c:/temp/test.cpp" ":"
5

这是一个无效的字符串,因为末尾有一个未被双引号包含的整数。

怎么办?别着急,一个特殊的预编译指示器“#”能够帮我们将一个变量转换成字符串。所以重新定义宏:


 
 
#define  AT __FILE__ ":" #__LINE__


嘿嘿,这样总行了吧。别高兴得太早,这样也是不行的。因为编译器会抱怨#是个无效字符。其实,问题是#预编译指示器只有这样使用才会
被正确识别:

#define  symbol(X) #X 


因此,我把代码改为:

#define  STRINGIFY(x) #x
#define  AT __FILE__ ":" STRINGIFY(__LINE__) 


然而,奇怪的结果产生了,__LINE__居然被作为了输出的一部分:

c:/temp/test.cpp:__LINE__: fake error

解决方法是再用一个宏来包装STRINGIFY():

#define  STRINGIFY(x) #x
#define  TOSTRING(x) STRINGIFY(x)
#define  AT __FILE__ ":" TOSTRING(__LINE__)


OK,我们用下面的代码来试试:

#include  < stdio.h >
#define  STRINGIFY(x) #x
#define  TOSTRING(x) STRINGIFY(x)
#define  AT __FILE__ ":" TOSTRING(__LINE__)
void  error( const   char   * location,  const   char   * msg)
{
  printf(
" Error at %s: %s/n " , location, msg);
}
int  main( int  ,  char ** )
{
  error(AT, 
" fake error " );
  
return   0 ;
}


输出结果:

Error at c:/temp/test/test.cpp:11: fake error

Visual Studio下的实践
在《Windows核心编程》中,Jeffrey Richter提供了下面的宏在编译期输出有用信息:

#define  chSTR2(x) #x
#define  chSTR(x)  chSTR2(x)
#define  chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)

message是一个预编译指令,上面宏的使用方法是:

#pragma chMSG(Fix  this  later)


结论

  1. 预编译指示器__FILE__和__LINE__能够提供有用的信息。
  2. __LINE__的处理方式远比我们想象的要复杂。
  3. __FILE__被处理成字符串,给我们带来了不少方便。

摘自:http://www.cppblog.com/heath/archive/2008/08/05/58046.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用__FILE__和__LINE__定位错误 的相关文章

  • 无人驾驶感知篇之融合(五)

    今天早上看到上海新增一万七千左右 xff0c 看的真的很揪心 xff01 希望白衣战士能早点战胜这场疫情 xff0c 期待明天能有好消息 xff01 今天具体讲讲多贝叶斯估计算法的原理 xff0c 多贝叶斯估计法的主要思想是将传感器信息依据
  • MAC地址的介绍(单播、广播、组播、数据收发)

    MAC地址组成 网络设备的MAC地址是全球唯一的 MAC地址长度为48比特 xff0c 通常用十六进制表示 MAC地址包含两部分 xff1a 前24比特是组织唯一标识符 xff08 OUI xff0c OrganizationallyUni
  • stm32通用定时器输出PWM控制舵机

    stm32的通用定时器有TIM2 TIM3 TIM4 TIM5 xff0c 每个定时器都有独立的四个通道可以作为 xff1a 输入捕获 输出比较 PWM输出 单脉冲模式输出等 stm32除了基本定时器 xff0c 其他定时器都能输出PWM
  • Linux内核Socket CAN中文文档

    自己在年假中空闲之余翻译的内核中Socket CAN的文档 xff0c 原文地址在 xff1a http lxr linux no linux 43 v2 6 34 Documentation networking can txt 但是这篇
  • c/c++自定义通讯协议(TCP/UDP)

    前言 xff1a TCP与UDP是大家耳熟能详的两种传输层通信协议 xff0c 本质区别在于传输控制策略不相同 xff1a 使用TCP协议 xff0c 可以保证传输层数据包能够有序地被接受方接收到 xff0c 依赖其内部一系列复杂的机制 x
  • ubuntu 使用虚拟can 与 socketCAN使用

    原文链接 xff1a https blog csdn net xiandang8023 article details 127990159 创建虚拟CAN接口 在Linux上能使用虚拟CAN接口之前 xff0c 需要在终端执行以下三个步骤
  • cmake引入第三方库

    cmake引入第三方库 第三方库包含 lib文件和 h hpp文件动态库还包含 dll文件 小例程 3rdparty bin test dll include test hpp lib Debug test lib Release test
  • AHB-APB总线协议

    AHB APB总线协议 文章目录 AHB APB总线协议一 AHB APB总线介绍二 AHB总线设备1 AHB主设备 xff08 master xff09 2 AHB从设备 xff08 slave xff09 3 AHB仲裁器 xff08
  • Modelsim缺失库快速添加

    Modelsim缺失库快速添加 文章目录 Modelsim缺失库快速添加前言一 ini文件二 器件库配置1 将器件库放在modelsim文件夹下2 ini配置文件修改 前言 在单独使用modelsim时 xff0c 假如要编译复杂的工程文件
  • AHB-APB_Lite总线协议及Verilog实现

    AHB APB Lite总线协议及Verilog实现 文章目录 AHB APB Lite总线协议及Verilog实现一 AHB Lite协议介绍二 系统框架介绍三 代码设计四 仿真测试 一 AHB Lite协议介绍 AHB xff08 Ad
  • 通信协议详解(二):IIC总线协议(传输时序+数据格式+设计实现)

    文章目录 一 IIC xff08 Inter Integrated Circuit xff09 介绍二 传输协议1 时序传输时序写操作时序数据有效性开始 amp 结束信号从机应答信号 2 数据格式 三 设计实现1 时钟2 传输过程3 三态门
  • Qt error ------ 'XXX' has not been declared

    1 头文件没加 2 调用函数者的头文件在XXX头文件的下方 转载于 https www cnblogs com god of death p 8572306 html
  • Command Expert安装

    一 安装准备 需先下载两个安装包 1 Commmand Expert安装包 https www keysight com cn zh lib software detail computer software command expert
  • Vitis开发(一):Vivado启动vitis

    Vitis是Xilinx SDK的继承开发工具 xff0c 从Vivado 2019 2版本开始启用 在Vivado 2019 1及更早版本中 xff0c 导出的硬件描述文件为 hdf文件 xff0c 给xilinx sdk使用 在Viva
  • 数字IC刷题(一)

    一 选择 1 To achieve better leakage cells are placed A HVT B LVT C RVT 解 LVT Low V threshold xff1a 低阈值 这种库的漏电流较大 xff0c 但是延迟
  • CPU设计-分支预测

    目录 CPU分支指令类型分类 1 xff09 无条件跳转 xff0f 分支 xff08 Unconditional Jump Branch xff09 指令 无条件直接跳转 xff0f 分支 xff1a 无条件间接跳转 xff0f 分支 x
  • 数字世界的积木-从MOS管搭反相器,与非门,锁存器,触发器

    文章目录 一 MOS管MOS管搭建反相器MOS管搭建传输门MOS管搭建与非门 二 与非门R S锁存器三 电平触发器电平触发RS锁存器带异步复位 xff0c 异步置位的电平触发RS锁存器电平触发D触发器 四 边沿触发器 一 MOS管 NMOS
  • 内存对齐规则

    每个特定平台上的编译器都有自己的默认 对齐系数 也叫对齐模数 程序员可以通过预编译命令 pragma pack n xff0c n 61 1 2 4 8 16来改变这一系数 xff0c 其中的n就是你要指定的 对齐系数 规则 xff1a 1
  • EGO-Planner: An ESDF-free Gradient-based Local Planner for Quadrotors(论文笔记)

    EGO Planner An ESDF free Gradient based Local Planner for Quadrotors xff08 论文笔记 xff09 ESDF欧式符号距离场避碰力估算基于梯度的轨迹优化平滑惩罚障碍惩罚可
  • 计算机组成原理系列(三):计算机存储器结构体系详解

    x1f525 Hi xff0c 我是小余 本文已收录到 GitHub Androider Planet 中 这里有 Android 进阶成长知识体系 xff0c 关注公众号 小余的自习室 xff0c 在成功的路上不迷路 xff01 前言 生

随机推荐