C++ std::source_location

2025年2月11日 | 阅读 7 分钟

概述

C++20 引入了 source_location(源位置)这一术语,它是一个用于确定源代码详细信息的工具,包括文件名、函数名、行号和列号。它的主要应用是在程序调试、日志记录和诊断过程中。这项功能对于希望在不导入额外信息的情况下获取特定代码行所在源代码位置信息的开发人员非常有用。典型用法是将 `std::source_location::current()` 作为函数的默认参数传递,这种技巧可以捕获调用者的源代码位置。这使得代码库更易于维护和阅读,因为源代码信息的收集可以自动化,这对于跟踪和调试非常有帮助。列出的实用函数和成员是 `file_name()`、`function_name()`、`line()` 和 `column()`,它们分别提供相应的信息。总而言之,`std::source_location` 增强了在 C++ 应用程序中提供上下文感知诊断的能力。

std::source_location 类

  • std::source_location 类: C++20 标准在 `` 头文件中新提出的一种实用工具,它本质上是一个用于存储实例在源代码中起源信息的类。该类包含几个成员函数,用于返回有关源代码位置的特定详细信息。
  • file_name(): 此成员函数返回定义函数或表达式的源文件名。返回值是一个 `std::string` 对象,可以快速处理并嵌入到日志记录或诊断消息中。
  • line(): 此函数返回我们正在编译的函数或表达式在源文件中存在的行号。它返回一个无符号整数,因此行号始终是正数,这在源代码跟踪中非常有帮助。
  • column(): 此成员函数用于给出函数或表达式实现所在的源文件的列号。它是一个无符号整数,可以精确地指示行内指针的水平位置,这对于在代码中进行精确的字符串定位非常有用。
  • function_name(): 使用此函数可以返回包含 `std::source_location` 对象的函数名称。返回值是一个 `std::string` 对象;函数的确切名称允许在给定上下文中进行更精确的诊断。
  • current(): 这是一个静态成员函数,它返回一个 `std::source_location` 类型的源位置对象,该对象对应于源代码中当前的函数调用。默认情况下,`current()` 会处理活动和调用站点信息,并且 `current()` 可以自动准确地记录调用站点信息,而无需程序员指定。

语法

它具有以下语法:

源位置示例

示例 1

输出

 
Function: void foo()
File: main.cpp
Line: 11
Column: 51   

示例 2

输出

 
main.cpp:9 (void foo()) Hello, world!   

std::source_location 的特性

  • 自动源代码位置跟踪: `std::source_location` 使我们能够自动获取源代码详细信息,指示其使用所在的文件、函数、行和列。这节省了手动查找和记录这些特征的时间和精力。
  • 静态成员函数 current(): 静态成员函数 `current()` 生成一个 `std::source_location` 对象,该对象包含当前函数或方法被调用的代码位置。这使得在无需额外样板代码的情况下快速获取源位置信息成为可能。
  • 详细的源代码信息: 该类提供了多个成员函数来检索源代码位置的各个方面:该类提供了多个成员函数来检索源代码位置的各个元素。
    • file_name(): 以 C++ 约定 `std::string` 的形式返回源文件名。
    • line(): 以无符号整数的形式返回源文件中的行号,该整数表示由文件名标识的源文件中的行索引。
    • column(): 在行号前加上源文件中的列号,并返回无符号整数形式的列号。
    • function_name(): 以以 null 字符结尾的字符数组或 `std::string` 的引用形式返回函数名称。
  • 与日志记录和诊断工具集成: `std::source_location` 可以很好地集成到日志记录和诊断中;这意味着在开发软件时,程序员可以使用该机制来指定更具上下文感知能力的消息,从而更容易跟踪和调试潜在问题。
  • 增强代码的可读性和可维护性: 通过使用 `std::source_location`,这意味着他们能够最大限度地减少工具生成的代码复杂性,从而提高代码的可读性和可维护性。它会自动提供完整的源代码信息,并有助于避免代码库中充斥着标准代码。
  • 在默认参数中的用法: 它可以作为参数在函数中使用,在不修改调用方操作的情况下保存调用方的源代码位置。这对于日志记录和调试函数特别有用,因为如果它们被互换使用,它们将产生相同的结果。

std::source_location 的缺点

  • 可用性有限: 编译器支持:`std::source_location` 是 C++20 中引入的一项 C++ 功能,因此,它可能不被不支持 C++20 的旧编译器或环境支持。这将其使用限制在那些使用支持 C++20 版本的当前编译器编译的项目中。
  • 开销: 性能开销:集成 `std::source_location` 也可能导致整体性能的显著开销,尤其是在高消耗的应用中。捕获和处理源位置信息也可能需要成本,特别是当其捕获和使用频率很高时,例如在高频日志记录中。
  • 代码膨胀: 如果源代码信息被放置在许多地方,由于存储了额外的数据,二进制文件的大小可能会显著增加。
  • 有限的使用场景: 因此,在需要高性能和小型二进制文件大小的应用中,使用 `std::source_location` 可能是不可取的。使用此类功能的主要原因是用于调试和诊断程序运行问题,因此,很明显,除了这个目的之外,这些基本功能可能不会提供增强的用户效用。
  • 静态信息: `std::source_location` 是一种关于源代码位置的静态信息。它无法获取当前的运行时上下文,包括变量的状态或下一行代码的调用堆栈。
  • 兼容性问题: 将 `std::source_location` 添加到现有的日志记录和诊断系统中可能存在潜在问题:这可能并不直接。一些日志框架可能没有考虑到源代码信息的设计,这意味着需要进行一些更改。
  • 学习曲线: 任何从事开发工作的人都必须了解这项新功能以及如何在系统中最好地利用它。可能有一些步骤略有不同,尤其是在不熟悉 C++20 功能的情况下。
  • 特定于编译器的行为: 我们已经从不同编译器的 `std::source_location` 实现中遇到过同样的情况——它们可能会以略有不同的方式捕获不同类型的信息。不同编译器行为中的细微波动可能会出现,需要进行额外的测试和验证。

结论

总而言之,在 C++20 中引入的 `std::source_location` 在捕获文件名、函数名、行号和列号的源代码条件方面非常有用。主要用于调试、日志记录和诊断,获取和使用源代码位置数据的过程,从而提高了代码的可维护性和可读性。通过 `std::source_location::current()` 自动捕获此信息。因此,通过稍微多一点努力,开发人员就可以创建上下文感知的诊断消息。

然而,`std::source_location` 像所有其他源位置一样,也有一些缺点。它与使用 C++20 支持环境相关,并且可能降低性能并增加代码大小。因此,它在调试以外的使用场景中用处较小。此外,它收集的数据是固定的,并且可能需要额外的工作来连接到其他数据。开发人员还应考虑与学习新的 C++20 元素和不同编译器行为相关的开销。尽管存在这些限制,`std::source_location` 今天仍然可以用于提高现代 C++ 应用程序中调试器的质量和功能。