【问题标题】:Is there a way to get the line number with out a macro in c++?有没有办法在c ++中没有宏来获取行号?
【发布时间】:2023-03-25 04:18:01
【问题描述】:

我正在编写一个记录器,我希望记录器还记录调用它的行、函数和文件。我已经尝试过#define my_logger(format, ...) _log(format, __LINE__, __PRETTY_FUNCTION__, __FILE__, __VA_ARGS__),但是它可以工作,这给我带来了几个问题(我不能轻易地重载它,我不能把它放在一个命名空间中,不是最便携的等等)。

有没有一种方法可以声明像void my_logger(const char* format, ...) 这样的普通函数,并且在函数定义中让它知道从与__LINE_FUNCTION_WAS_CALLED_FROM__ 等效的东西调用它的行(如果存在这样的宏)?至少它需要在 GCC 上运行,并且确实应该是跨平台的。

编辑:我所说的跨平台是指 x86 和 ARM 上的 Linux 和 Windows

【问题讨论】:

  • @Chipster 我不想使用堆栈跟踪,因为我希望记录器即使在发布版本中也能获得信息,并且我不希望堆栈跟踪的开销
  • 这是有道理的。我不认为这正是你想要的,但我只是想我会提到它,因为如果需要的话,你应该能够以这种方式完成同样的事情。当然,这种方式无论如何也不能跨平台移植。
  • 你能负担多少年的工作花费在你的项目上?

标签: c++ gcc


【解决方案1】:

不能有__LINE_FUNCTION_WAS_CALLED_FROM__ 这样的宏。没办法。

从 C++20 开始,有一种方法可以通过传递默认初始化的 std::source_location 来将行、函数等转换为普通函数:

void log(std::string_view message,
         std::source_location location = std::source_location::current());

但是,由于它依赖于默认参数,因此可变参数实现起来有点棘手:How to use source_location in a variadic template function?

在 C++20 之前,宏是您唯一的可移植选项。


我看到您在宏中调用了一个名为 _log 的函数。该标识符保留给全局命名空间中的语言实现。您可能应该为该函数指定另一个名称以避免未定义的行为。

log 也保留在全局命名空间中。无论如何,您都应该在自定义命名空间中声明所有名称(自然而然的自定义命名空间除外)。


[宏是] ...不是最便携的

如果您要将__PRETTY_FUNCTION__ 替换为__func__,则该宏将可移植到所有符合标准的编译器——尽管__func__ 给出的确切字符串可能因实现而异。

【讨论】:

【解决方案2】:

有没有一种方法可以声明一个普通函数,如 void my_logger(const char* format, ...) 并在函数定义中让它知道调用它的行,如果这样的话宏存在吗?

一般不会。

在 2020 年的 Linux x86-64 上,all 您的(或其他)C++ 代码(C++11 或更高版本)使用 DWARF 调试信息编译(所以如果使用最近的 @ 987654322@,带有g++ -g -Wall,如果使用最近的Clang,带有clang++ -g -Wall),您可以使用Ian Taylor 的libbacktrace 库。

另外,一个好的optimizing compiler 允许inline expandunroll loop (实际上g++ -O2clang++ -O2 会同时做这两个,微软编译器也是如此),然后你的__LINE_FUNCTION_WAS_CALLED_FROM__ 没有意义.

作为一个开源示例,RefPerSys 正在使用它来显示回溯和部分调用堆栈。免责声明:RefPerSys 是我自己的项目。

注意与共享库相关的ASLR。加载了dlopen(3) 的插件会增加图片的复杂性。

它至少需要在 GCC 上运行,并且确实应该是跨平台的。

不能跨平台。某些架构(例如IBM Z 系列)有不同的calling conventions,您需要努力(或付费)将libbacktrace 移植到它们。

您可能会考虑在某些系统上进行调试,然后将您的代码移植到其他系统上,并且您应该考虑将 libSFMLQt 等框架用于您的游戏引擎。我的意见是建议在 Linux 上调试您的代码(以使其正确),然后将其移植到 Windows。

如果您需要cross-compilation,事情就会变得更加困难。您也可以考虑metaprogramming 方法:编写您的 C++ 代码生成器或使用其他代码生成器(例如 SWIGANTLR

【讨论】:

  • 尽管我很喜欢 Linux 大师赛,但我正在编写一个游戏引擎,不幸的是需要支持 windows。作为其中的一部分,我想支持 MSVC 而不是将 Windows 构建锁定到 MinGW。我查看了堆栈跟踪并不想使用它,因为我希望即使在构建版本中也能够让记录器跟踪调用位置,但我不确定堆栈跟踪会带来什么性能损失。
  • 我的建议:在 Linux 上编译(使用 g++ -Wall -Wextra -g)。调试代码后,将其移植到 Windows。 首先使您的代码在一台计算机和操作系统上正确且无错误。然后花精力进行优化并将其移植到其他平台。
猜你喜欢
  • 2012-04-29
  • 2011-07-24
  • 1970-01-01
  • 2016-02-18
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
相关资源
最近更新 更多