【发布时间】:2016-03-28 03:09:24
【问题描述】:
用于库基础的 C++ 扩展,第 2 版 (N4564) 引入了 std::experimental::source_location 类型。
§ 14.1.2 [reflection.src_loc.creation] 说:
static constexpr source_location current() noexcept;返回:当被函数调用(C++14 §5.2.2)调用时,postfix-expression 是一个(可能带括号的)id-表达式 命名
current,返回一个带有实现定义值的source_location。该值应受#line(C++14 §16.4) 的影响,其方式与__LINE__和__FILE__相同。如果以其他方式调用,返回的值是未指定的。备注:当使用 brace-or-equal-initializer 来初始化非静态数据成员时,任何对
current的调用都应对应于位置初始化成员的构造函数或聚合初始化。[ 注意: 当用作默认参数时(C++14 §8.3.6),
source_location的值将是调用current的位置呼叫站点。 — 尾注 ]
如果我理解正确,那么该功能就是打算这样使用的。
#include <experimental/source_location> // I don't actually have this header
#include <iostream>
#include <string>
#include <utility>
struct my_exception
{
std::string message {};
std::experimental::source_location location {};
my_exception(std::string msg,
std::experimental::source_location loc = std::experimental::source_location::current()) :
message {std::move(msg)},
location {std::move(loc)}
{
}
};
int
do_stuff(const int a, const int b)
{
if (a > b)
throw my_exception {"a > b"}; // line 25 of file main.cxx
return b - a;
}
int
main()
{
try
{
std::cout << do_stuff(2, 1) << "\n";
}
catch (const my_exception& e)
{
std::cerr << e.location.file_name() << ":" << e.location.line() << ": "
<< "error: " << e.message << "\n";
}
}
预期输出:
main.cxx:25: error: a > b
如果没有std::experimental::source_location,我们可能会使用辅助宏THROW_WITH_SOURCE_LOCATION,它在内部使用__FILE__ 和__LINE__ 宏来正确初始化异常对象。
我想知道一个库如何实现std::experimental::source_location。除非我完全忽略了这一点,否则如果没有特殊的编译器支持,这样做是不可能的。但是需要什么样的魔法编译器功能才能使这项工作?它可以与为std::initializer_list 部署的技巧相媲美吗?是否有此功能的实验性实现可供查看?我检查了the SVN sources for GCC,但还没有找到任何东西。
【问题讨论】:
-
编译器魔法,根据
std::type_info -
@RichardHodges
typeid可以实现为一个相当直接的函数。对于非多态类型,编译器已经知道答案可以直接插入字符串文字,对于多态类型,它必须在运行时生成简单的代码来遵循vptr。我看不出source_location的实现如何同样简单。但如果你知道,那么这正是我正在寻找的答案。 -
但是在没有编译器支持的情况下,如何仅使用库获取类型的名称?在 type_traits 中还有许多需要编译器魔法的助手。例如
is_class/enum/union. -
它要做的不仅仅是生成简单的代码——它还必须生成数据结构。尽管如此,编译器还是可以访问行号和文件名(这些已经被注入到源代码中,这样
__FILE__和__LINE__就可以工作了。std::source_location所做的只是将与用户代码的关系形式化。就像编译器/标准库适用于std::initializer_list(另一个“魔术”类) -
@5gon12eder 当然是。否则
std::cout << __LINE__ << std::endl将如何工作?请记住,在扩展预处理器指令(例如#include)之后,所有实际的行号都发生了变化,但我们的代码报告了我们期望的行号。预处理器和编译器阶段之间存在松散但密切的关系。
标签: c++ reflection c++17 fundamentals-ts std-source-location