如何在运行时找到当前函数的名称?

发布于 2024-07-16 07:34:14 字数 655 浏览 9 评论 0原文

在使用又大又丑的 MFC ASSERT 宏多年之后,我终于决定放弃它并创建终极的 ASSERT 宏。

我可以很好地获取文件和行号,甚至失败的表达式。 我可以显示一个消息框,其中包含中止/重试/取消按钮。

当我按“重试”时,VS 调试器会跳转到包含 ASSERT 调用的行(而不是像其他 ASSERT 函数那样进行反汇编)。 所以这一切都非常有效。

但真正酷的是显示失败函数的名称

然后我可以决定是否调试它,而无需尝试从文件名猜测它的函数。

例如,如果我有以下函数:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   ASSERT(lpCreateStruct->cx > 0);
   ...
}

那么当 ASSERT 触发时,消息框将显示类似以下内容:

Function = CMainFrame::OnCreate

那么,在运行时查找当前函数名称的最简单方法是什么?

它应该不使用 MFC 或 .NET 框架,尽管我确实使用了这两个框架。
它应该尽可能便携。

After years of using the big ugly MFC ASSERT macro, I have finally decided to ditch it and create the ultimate ASSERT macro.

I am fine with getting the file and line number, and even the expression that failed. I can display a messagebox with these in, and Abort/Retry/Cancel buttons.

And when I press Retry the VS debugger jumps to the line containing the ASSERT call (as opposed to the disassembly somewhere like some other ASSERT functions). So it's all pretty much working.

But what would be really cool would be to display the name of the function that failed.

Then I can decide whether to debug it without trying to guess what function it's in from the filename.

e.g. if I have the following function:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   ASSERT(lpCreateStruct->cx > 0);
   ...
}

Then when the ASSERT fires, the messagebox would show something like:

Function = CMainFrame::OnCreate

So, what's the simplest way of finding out the current function name, at runtime?

It should not use MFC or the .NET framework, even though I do use both of these.
It should be as portable as possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

铁轨上的流浪者 2024-07-23 07:34:15

在 GCC 中,您可以使用 __PRETTY_FUNCTION__ 宏。
微软也有一个等效的 __func__ 宏,尽管我没有可以尝试。

例如,使用 __PRETTY_FUNCTION__ 在函数的开头放置类似的内容,您将获得完整的跟踪,

void foo(char* bar){
  cout << __PRETTY_FUNCTION__ << std::endl
}

该跟踪将输出

void foo(char* bar)

您还拥有 __FILE____LINE__<如果您想输出更多信息,/code> 宏可在所有标准 c/c++ 编译器下使用。

在实践中,我有一个特殊的调试类,我用它来代替 cout。 通过定义适当的环境变量,我可以获得完整的程序跟踪。 你可以做类似的事情。 这些宏非常方便,能够在现场像这样打开选择性调试真是太棒了。

编辑:显然 __func__ 是标准的一部分? 我不知道。 不幸的是,它只给出了函数名称,而没有给出参数。 我确实喜欢 gcc 的 __PRETTY_FUNC__ 但它不能移植到其他编译器。

GCC 还支持 __FUNCTION__

In GCC you can use the __PRETTY_FUNCTION__ macro.
Microsoft also have an equivalent __func__ macro although I don't have that available to try.

e.g. to use __PRETTY_FUNCTION__ putting something like this at the beginning of your functions and you'll get a complete trace

void foo(char* bar){
  cout << __PRETTY_FUNCTION__ << std::endl
}

which will output

void foo(char* bar)

You also have the __FILE__ and __LINE__ macros available under all standard c/c++ compilers if you want to output even more information.

In practice I have a special debugging class which I use instead of cout. By defining appropriate environment variables I can get a full program trace. You could do something similar. These macros are incredibly handy and it's really great to be able to turn on selective debugging like this in the field.

EDIT: apparently __func__ is part of the standard? didn't know that. Unfortunately, it only gives the function name and not the parameters as well. I do like gcc's __PRETTY_FUNC__ but it's not portable to other compilers.

GCC also supports __FUNCTION__.

陌路终见情 2024-07-23 07:34:15

您可以使用 __FUNCTION__ 在编译时将扩展为函数的名称。

下面是如何在断言宏中使用它的示例。

#define ASSERT(cond) \
    do { if (!(cond)) \
    MessageBoxFunction("Failed: %s in Function %s", #cond, __FUNCTION__);\
    } while(0)

void MessageBoxFunction(const char* const msg,  ...)
{
    char szAssertMsg[2048];

    // format args
    va_list vargs;
    va_start(vargs, msg);
    vsprintf(szAssertMsg, msg, vargs);
    va_end(vargs);

    ::MessageBoxA(NULL, szAssertMsg, "Failed Assertion", MB_ICONERROR | MB_OK);
}

You can use the __FUNCTION__ macro which at compile time will be expanded to the name of the function.

Here's an example of how to use it in an assert macro.

#define ASSERT(cond) \
    do { if (!(cond)) \
    MessageBoxFunction("Failed: %s in Function %s", #cond, __FUNCTION__);\
    } while(0)

void MessageBoxFunction(const char* const msg,  ...)
{
    char szAssertMsg[2048];

    // format args
    va_list vargs;
    va_start(vargs, msg);
    vsprintf(szAssertMsg, msg, vargs);
    va_end(vargs);

    ::MessageBoxA(NULL, szAssertMsg, "Failed Assertion", MB_ICONERROR | MB_OK);
}
趁年轻赶紧闹 2024-07-23 07:34:15

C++20 std::source_location::function_name

现在我们已经有了适当的标准化,所以不需要宏:

main.cpp

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
         const std::source_location& location = std::source_location::current()
) {
    std::cout << "info:"
              << location.file_name() << ":"
              << location.line() << ":"
              << location.function_name() << " "
              << message << '\n';
}

int f(int i) {
    log("Hello world!"); // Line 16
    return i + 1;
}

int f(double i) {
    log("Hello world!"); // Line 21
    return i + 1.0;
}

int main() {
    f(1);
    f(1.0);
}

编译并运行:

g++ -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o source_location.out source_location.cpp
./source_location.out

输出:

info:source_location.cpp:16:int f(int) Hello world!
info:source_location.cpp:21:int f(double) Hello world!

所以请注意如何调用保留调用者信息,因此我们看到所需的 main 调用位置而不是 log

我在以下位置更详细地介绍了相关标准: __PRETTY_FUNCTION__、__FUNCTION__、__func__ 之间有什么区别?

在 Ubuntu 22.04、GCC 11.3 上测试。

C++20 std::source_location::function_name

No macros are needed now that we have proper standardization:

main.cpp

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
         const std::source_location& location = std::source_location::current()
) {
    std::cout << "info:"
              << location.file_name() << ":"
              << location.line() << ":"
              << location.function_name() << " "
              << message << '\n';
}

int f(int i) {
    log("Hello world!"); // Line 16
    return i + 1;
}

int f(double i) {
    log("Hello world!"); // Line 21
    return i + 1.0;
}

int main() {
    f(1);
    f(1.0);
}

Compile and run:

g++ -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o source_location.out source_location.cpp
./source_location.out

Output:

info:source_location.cpp:16:int f(int) Hello world!
info:source_location.cpp:21:int f(double) Hello world!

so note how the call preserves caller information, so we see the desired main call location instead of log.

I have covered the relevant standards in a bit more detail at: What's the difference between __PRETTY_FUNCTION__, __FUNCTION__, __func__?

Tested on Ubuntu 22.04, GCC 11.3.

奢望 2024-07-23 07:34:15

您可以轻松使用func
它会在运行时取回引发异常的当前函数名称。

用法:

cout << __func__ << ": " << e.what();

you can easily use func.
it will take back you current function name at runtime which raised the exception.

usage:

cout << __func__ << ": " << e.what();
等风来 2024-07-23 07:34:14

您的宏可以包含 __FUNCTION__ 宏。
毫无疑问,函数名称将在编译时插入到扩展代码中,但对于每次调用宏来说,它将是正确的函数名称。 所以它“看起来”是在运行时发生的;)

例如

#define THROW_IF(val) if (val) throw "error in " __FUNCTION__

int foo()
{
    int a = 0;
    THROW_IF(a > 0); // will throw "error in foo()"
}

Your macro can contain the __FUNCTION__ macro.
Make no mistake, the function name will be inserted into the expanded code at compile time, but it will be the correct function name for each call to your macro. So it "seems like" it happens in run-time ;)

e.g.

#define THROW_IF(val) if (val) throw "error in " __FUNCTION__

int foo()
{
    int a = 0;
    THROW_IF(a > 0); // will throw "error in foo()"
}
王权女流氓 2024-07-23 07:34:14

C++ 预处理器宏 __FUNCTION__ 给出了函数的名称。

请注意,如果您使用此方法,它实际上并不能在运行时获取文件名、行号或函数名称。 宏由预处理器扩展并编译。

示例程序:

#include <iostream>

void function1()
{
        std::cout << "my function name is: " << __FUNCTION__ << "\n";
}

int main()
{
        std::cout << "my function name is: " << __FUNCTION__ << "\n";
        function1();
        return 0;
}

输出:

my function name is: main
my function name is: function1

The C++ preprocessor macro __FUNCTION__ gives the name of the function.

Note that if you use this, it's not really getting the filename, line number, or function name at runtime. Macros are expanded by the preprocessor, and compiled in.

Example program:

#include <iostream>

void function1()
{
        std::cout << "my function name is: " << __FUNCTION__ << "\n";
}

int main()
{
        std::cout << "my function name is: " << __FUNCTION__ << "\n";
        function1();
        return 0;
}

output:

my function name is: main
my function name is: function1
活雷疯 2024-07-23 07:34:14

没有标准的解决方案。 但是,BOOST_CURRENT_FUNCTION对于所有实际目的都是可移植的。 该标头不依赖于任何其他 Boost 标头,因此如果整个库的开销不可接受,则可以独立使用。

There's no standard solution. However, BOOST_CURRENT_FUNCTION is portable for all practical purposes. The header does not not depend on any of the other Boost headers, so can be used standalone if the overhead of the whole library is unacceptable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文