c++23中的新功能之五堆栈信息库

一、介绍

一般对开发者而言,明晓程序中函数堆栈的调用级次是非常有用的。特别是在遇到一些问题时,可以知道问题产生的函数和此函数被调用的层次,这样就可以基本准确的确定Bug的范围了。所以一个强大的IDE,是所有开发者的一个梦想。也是因为这种情况Vistual Studio被称做是“宇宙第一IDE”也不是没有原因的。特别是当和其它的IDE比较时,会发现,这个开发IDE的真得很强大。没有比较就没有伤害。
但是问题来了,这个堆栈信息如何得来?能不能更轻松的为开发者所获取?
在GCC中有一个函数“__builtin_return_address(n)”,通过后面这个n,来得调用级联函数的地址,比如0是当头大哥,1是你当头大哥的当头大哥…。当然,这个builtin函数是一系列的,不是一个,有兴趣可以试一下,在一些特定情况下还是非常有用的。
另外还有几个函数(execinfo.h):

int backtrace (void **buffer, int size);
char **backtrace_symbols (void *const *buffer, int size);
void backtrace_symbols_fd (void *const *buffer, int size, int fd);

也可以实现堆栈的调用信息查看。
可以看个例子:

#include 
#include 
#include 
#include 
#include 

void StackTrace(void)
{
    int i, size;
    void * buf[128];
    size = backtrace(buf, 128);
    char ** str = backtrace_symbols(buf, size);
    for (i = 0; i < size; i++)
    {
        printf("cur stack :%d# %s\n",i, str[i]);
    }
    free(str);
}

void DisplayStack()
{
    StackTrace();
}

void CallFunc()
{
    DisplayStack();
}

int main()
{
    CallFunc();
}

运行结果:

cur stack :0# ./st(+0x11fb) [0x5580e78d21fb]
cur stack :1# ./st(+0x12ac) [0x5580e78d22ac]
cur stack :2# ./st(+0x12bc) [0x5580e78d22bc]
cur stack :3# ./st(+0x12cc) [0x5580e78d22cc]
cur stack :4# /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7fbcc5629d90]
cur stack :5# /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7fbcc5629e40]
cur stack :6# ./st(+0x1105) [0x5580e78d2105]

这其实也是对STL中只提供断言(assert()等)的一个补充,一切都向着方便易用在发展嘛。

二、c++23中的堆栈信息库

其实这就是Boost.Stacktrace在试用后转正到了STL中,要不老说Boost是STL的前置库不是没有道理的。在c++23中,提出了std::basic_stacktrace:

Defined in header 
template< class Allocator >
class basic_stacktrace;
(1)	(since C++23)
using stacktrace =
    std::basic_stacktrace>;
(2)	(since C++23)
namespace pmr {
using stacktrace =
    std::basic_stacktrace>;
}

这等于给c++开发者又一个方便的工具,和前面提到的c++20中的source_location都算是一个工具类的函数。
这个stacktrace是一个容器对象,从前面的分析其实也可以猜测出来,他一定是一层层的通过数据结构来存储着堆栈信息,那么最简单的方式就是用数组,在标准库是那么就是vector了,其中的数据结构是stacktrace_entry,有兴趣可以在“https://en.cppreference.com/w/cpp/utility/stacktrace_entry”看到这个条目的详细说明。在这个条目说明中,可以看条提供了to_string,operator<<和hash操作。
其实看到上面说明,是不是可以想一下,在开发者自己以后的开发中,如果有类似的需求,是不是可以拿来主义一下它的设计思想和设计方式呢?

三、应用

下面来看一个简单的例子:

#include 
#include 

int nested_func(int c)
{
    std::cout << std::stacktrace::current() << '\n';
    return c + 1;
}

int func(int b)
{
    return nested_func(b + 1);
}

int main()
{
    std::cout << func(777);
}

其运行结果为:

msvc output:
0> C:\Users\ContainerAdministrator\AppData\Local\Temp\compiler-explorer-compiler20221122-31624-2ja1sf.8ytzw\example.cpp(6): output_s!nested_func+0x1F
1> C:\Users\ContainerAdministrator\AppData\Local\Temp\compiler-explorer-compiler20221122-31624-2ja1sf.8ytzw\example.cpp(12): output_s!func+0x15
2> C:\Users\ContainerAdministrator\AppData\Local\Temp\compiler-explorer-compiler20221122-31624-2ja1sf.8ytzw\example.cpp(15): output_s!main+0xE
3> D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288): output_s!__scrt_common_main_seh+0x10C
4> KERNEL32!BaseThreadInitThunk+0x14
5> ntdll!RtlUserThreadStart+0x21
779

gcc output:
   0# nested_func(int) at /app/example.cpp:7
   1# func(int) at /app/example.cpp:13
   2#      at /app/example.cpp:18
   3#      at :0
   4#      at :0
   5#

779

不过可惜是还是那句话,测试这个,得把编译器的版本提到最新,不然是搞不定的。

四、总结

越是学习,越会发现,很多东西其实都是互相影响的。前有Boost,又有gcc扩展相关的堆栈处理,STL对这种有利于自己的事情不可能无动于衷。所以,现在看学习时,有些前辈和牛人推荐大家多看外面的世界,跨一些语言甚至学科,不是没有道理的。但是怎么掌握其中的度,就需要自己把握了。总不能一个学c++的学着学着最后成了一个捕鱼的。

你可能感兴趣的:(C++11,C++,c++,开发语言)