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

6rvt4ljy  于 2023-04-13  发布在  其他
关注(0)|答案(8)|浏览(185)

在使用了几年又大又丑的MFC ASSERT宏之后,我终于决定放弃它,创建一个终极的ASSERT宏。
我可以很好地获得文件和行号,甚至失败的表达式。我可以显示一个消息框,其中包含这些,以及中止/重试/取消按钮。
当我按下Retry时,VS调试器会跳转到包含ASSERT调用的行(而不是像其他ASSERT函数那样进行反汇编)。
但真正酷的是 * 显示失败的函数的名称 *。
然后我就可以决定是否调试它,而不必试图从文件名猜测它在哪个函数中。
例如,如果我有以下函数:

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

然后,当ASSERT触发时,消息框将显示如下内容:

Function = CMainFrame::OnCreate

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

  • 它不应该使用MFC或.NET框架,即使我使用这两个。

它应该尽可能地便携。*

pzfprimi

pzfprimi1#

您的宏可以包含__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()"
}
djmepvbi

djmepvbi2#

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
92vpleto

92vpleto3#

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

iqjalb3h

iqjalb3h5#

在GCC中,你可以使用__PRETTY_FUNCTION__宏。
微软也有一个等效的__func__宏,虽然我没有可用的尝试。
例如,使用__PRETTY_FUNCTION__,在函数的开头放置这样的内容,您将获得完整的跟踪

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

它将输出

void foo(char* bar)

如果你想输出更多的信息,你还可以在所有标准的c/c++编译器下使用__FILE____LINE__宏。
在实践中,我有一个特殊的调试类,我用它来代替cout。通过定义适当的环境变量,我可以得到完整的程序跟踪。你可以做类似的事情。这些宏非常方便,能够在现场打开这样的选择性调试真是太棒了。
编辑:显然__func__是标准的一部分?我不知道。不幸的是,它只给出了函数名,而不是参数。我确实喜欢gcc的__PRETTY_FUNC__,但它不能移植到其他编译器。
GCC还支持__FUNCTION__

vqlkdk9b

vqlkdk9b6#

您可以使用__FUNCTION__ macro,它在编译时将扩展为函数的名称。
下面是一个如何在Assert宏中使用它的例子。

#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);
}
8oomwypt

8oomwypt7#

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_FUNCTIONFUNCTION、__func__之间有什么区别?
在Ubuntu 22.04,GCC 11.3上测试。

acruukt9

acruukt98#

你可以很容易地使用func.它会在运行时取回你当前的函数名,这引发了异常.
使用方法:

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

相关问题