Исключение со стеком вызовов (stacktrace) в C++23

Исключение со стеком вызовов (stacktrace) в C++23 #

Большая сложность при разработке на C++ в том, что у исключений нет stacktrace. Впрочем, некоторые проекты на C++ вовсе избегают использования любых исключений.

Но если вы в своих проектах используете C++23 (или выше) и хотите иметь стек вызовов для некоторых типов исключений — эта статья для вас.

Заголовочный файл <stacktrace> #

В стандарте C++23 появился заголовочный файл <stacktrace>.

По состоянию на 21 марта 2026 года поддержка <stacktrace> выглядит так:

КомпиляторSTLСтатус
GCClibstdc++Поддерживается с версии 12.1
MSVCMicrosoft STLПоддерживается с выпуска VS 2022 17.4
Clanglibc++Не поддерживается

В большинстве дистрибутивов GNU/Linux используется libstdc++ и <stacktrace> будет работать, но если у вас в проекте используется libc++ — посмотрите в сторону Boost.Stacktrace.

Класс исключения с поддержкой stacktrace #

Допустим, мы назовём этот класс InternalErrorException и разместим его в пространстве имён diagnostics.

Будем добавлять стек вызовов к диагностическому сообщению исключения в таком формате:

{what}
stacktrace:
{stacktrace}

Заголовочный файл InternalErrorException.h:

#pragma once

#include <stdexcept>

namespace diagnostics
{
class InternalErrorException : public std::runtime_error
{
public:
    explicit InternalErrorException(const std::string& message);
};

} // namespace diagnostics

Файл реализации InternalErrorException.cpp:

#include "InternalErrorException.h"
#include <sstream>
#include <stacktrace>

namespace diagnostics
{
namespace
{
std::string appendStackTrace(const std::string& message)
{
    std::ostringstream os;
    os << message << "\nstacktrace:\n";
    os << std::stacktrace::current(1);

    return os.str();
}
} // namespace

InternalErrorException::InternalErrorException(const std::string& message)
    : std::runtime_error(appendStackTrace(message))
{
}
} // namespace diagnostics

Как это выглядит #

На GNU/Linux с GCC выше 12.1 результат печати этого исключения в консоль выглядит так:

attribute not set yet
stacktrace:
   0# diagnostics::InternalErrorException::InternalErrorException(std::
__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
const&) at /src/pstigercpp/src/
libdiagnostics/exceptions/InternalErrorException.cpp:20
   1# ast::AstAttribute<std::reference_wrapper<ast::BuiltinFunction const> >:
:get() const at /src/pstigercpp/src/libast/
expressions/../attributes/AstAttribute.h:27
   2# ast::FunctionCallExpression::function() const at /project/src//libast/expressions/FunctionCallExpression.
cpp:25
   [...]
   14# Catch::RunContext::invokeActiveTestCase() at :0
   15# Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std:
:char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> >&) at :0
   16# Catch::RunContext::runTest(Catch::TestCaseHandle const&) at :0
   17# Catch::Session::runInternal() at :0
   18# Catch::Session::run() at :0
   19# main at :0
   20# __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58
   21# __libc_start_main_impl at ../csu/libc-start.c:360
   22# _start at :0
   23# 

Объяснения:

  • я заменил полный путь к своему проекту на /project/
  • я заменил фреймы с номерами 3-13 на [...], чтобы не загромождать эту статью
  • основной текст исключения выглядит так: attribute not set yet
  • всё, идёт после stacktrace: — это результат вывода std::stacktrace в std::ostream средствами STL (libc++)

Сайт atdd.ru — блог разработчика.