Support building without Boost.Stacktrace

This commit is contained in:
Nikita Lisitsa 2025-01-26 14:45:43 +03:00
parent 4890761b0a
commit 6fc476f1f0
25 changed files with 95 additions and 69 deletions

View file

@ -2,12 +2,13 @@ file(GLOB_RECURSE RAPIDJSON_SOURCES "${CMAKE_CURRENT_LIST_DIR}/rapidjson/include
add_library(rapidjson INTERFACE EXCLUDE_FROM_ALL "${RAPIDJSON_SOURCES}") add_library(rapidjson INTERFACE EXCLUDE_FROM_ALL "${RAPIDJSON_SOURCES}")
target_include_directories(rapidjson INTERFACE "${CMAKE_CURRENT_LIST_DIR}/rapidjson/include") target_include_directories(rapidjson INTERFACE "${CMAKE_CURRENT_LIST_DIR}/rapidjson/include")
set(LIBBACKTRACE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/libbacktrace") if(PSEMEK_STACKTRACE)
set(LIBBACKTRACE_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/libbacktrace") set(LIBBACKTRACE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/libbacktrace")
set(LIBBACKTRACE_INCLUDE_DIR "${LIBBACKTRACE_BUILD_DIR}/include") set(LIBBACKTRACE_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/libbacktrace")
set(LIBBACKTRACE_LIBRARY "${LIBBACKTRACE_BUILD_DIR}/.libs/libbacktrace.a") set(LIBBACKTRACE_INCLUDE_DIR "${LIBBACKTRACE_BUILD_DIR}/include")
set(LIBBACKTRACE_LIBRARY "${LIBBACKTRACE_BUILD_DIR}/.libs/libbacktrace.a")
if(NOT EXISTS "${LIBBACKTRACE_BUILD_DIR}") if(NOT EXISTS "${LIBBACKTRACE_BUILD_DIR}")
file(COPY "${LIBBACKTRACE_SOURCE_DIR}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "${LIBBACKTRACE_SOURCE_DIR}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
make_directory("${LIBBACKTRACE_INCLUDE_DIR}") make_directory("${LIBBACKTRACE_INCLUDE_DIR}")
file(COPY "${LIBBACKTRACE_SOURCE_DIR}/backtrace.h" DESTINATION "${LIBBACKTRACE_INCLUDE_DIR}") file(COPY "${LIBBACKTRACE_SOURCE_DIR}/backtrace.h" DESTINATION "${LIBBACKTRACE_INCLUDE_DIR}")
@ -22,8 +23,10 @@ if(NOT EXISTS "${LIBBACKTRACE_BUILD_DIR}")
if(NOT (${LIBBACKTRACE_BUILD_RESULT} EQUAL 0)) if(NOT (${LIBBACKTRACE_BUILD_RESULT} EQUAL 0))
message(FATAL_ERROR "libbacktrace build failed") message(FATAL_ERROR "libbacktrace build failed")
endif() endif()
endif()
add_library(libbacktrace INTERFACE)
target_include_directories(libbacktrace INTERFACE "${LIBBACKTRACE_INCLUDE_DIR}")
target_link_libraries(libbacktrace INTERFACE "${LIBBACKTRACE_LIBRARY}")
endif() endif()
add_library(libbacktrace INTERFACE)
target_include_directories(libbacktrace INTERFACE "${LIBBACKTRACE_INCLUDE_DIR}")
target_link_libraries(libbacktrace INTERFACE "${LIBBACKTRACE_LIBRARY}")

View file

@ -72,6 +72,12 @@ endif()
message(STATUS "Using graphics API ${PSEMEK_GRAPHICS_API}") message(STATUS "Using graphics API ${PSEMEK_GRAPHICS_API}")
list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_GRAPHICS_API_${PSEMEK_GRAPHICS_API}=1") list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_GRAPHICS_API_${PSEMEK_GRAPHICS_API}=1")
option(PSEMEK_STACKTRACE "Use Boost.Stacktrace for stacktraces in exceptions" ON)
if(PSEMEK_STACKTRACE)
list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_STACKTRACE=1")
endif()
add_subdirectory(3rdparty) add_subdirectory(3rdparty)
get_directory_property(PSEMEK_PARENT_DIRECTORY PARENT_DIRECTORY) get_directory_property(PSEMEK_PARENT_DIRECTORY PARENT_DIRECTORY)

View file

@ -63,7 +63,7 @@ namespace psemek::async
struct empty_future_error struct empty_future_error
: util::exception : util::exception
{ {
empty_future_error(boost::stacktrace::stacktrace stacktrace = {}) empty_future_error(util::stacktrace stacktrace = {})
: util::exception("Future is empty", std::move(stacktrace)) : util::exception("Future is empty", std::move(stacktrace))
{} {}
}; };
@ -71,7 +71,7 @@ namespace psemek::async
struct canceled_task_error struct canceled_task_error
: util::exception : util::exception
{ {
canceled_task_error(boost::stacktrace::stacktrace stacktrace = {}) canceled_task_error(util::stacktrace stacktrace = {})
: util::exception("Task canceled", std::move(stacktrace)) : util::exception("Task canceled", std::move(stacktrace))
{} {}
}; };
@ -184,7 +184,7 @@ namespace psemek::async
struct empty_promise_error struct empty_promise_error
: util::exception : util::exception
{ {
empty_promise_error(boost::stacktrace::stacktrace stacktrace = {}) empty_promise_error(util::stacktrace stacktrace = {})
: util::exception("Promise is empty", std::move(stacktrace)) : util::exception("Promise is empty", std::move(stacktrace))
{} {}
}; };
@ -192,7 +192,7 @@ namespace psemek::async
struct satisfied_promise_error struct satisfied_promise_error
: util::exception : util::exception
{ {
satisfied_promise_error(boost::stacktrace::stacktrace stacktrace = {}) satisfied_promise_error(util::stacktrace stacktrace = {})
: util::exception("Promise already contains a value or exception", std::move(stacktrace)) : util::exception("Promise already contains a value or exception", std::move(stacktrace))
{} {}
}; };

View file

@ -11,7 +11,7 @@ namespace psemek::bt
struct assertion_failed_exception struct assertion_failed_exception
: util::exception : util::exception
{ {
assertion_failed_exception(std::string message, boost::stacktrace::stacktrace stacktrace = {}) assertion_failed_exception(std::string message, util::stacktrace stacktrace = {})
: util::exception(std::move(message), std::move(stacktrace)) : util::exception(std::move(message), std::move(stacktrace))
{} {}
}; };

View file

@ -13,7 +13,7 @@ namespace psemek::ecs::detail
struct duplicate_uuid_exception struct duplicate_uuid_exception
: util::exception : util::exception
{ {
duplicate_uuid_exception(util::uuid const & uuid, std::type_info const & type1, std::type_info const & type2, boost::stacktrace::stacktrace stacktrace = {}) duplicate_uuid_exception(util::uuid const & uuid, std::type_info const & type1, std::type_info const & type2, util::stacktrace stacktrace = {})
: util::exception(util::to_string("Found duplicate UUID ", uuid, " for components ", util::type_name(type1), " and ", util::type_name(type2)), std::move(stacktrace)) : util::exception(util::to_string("Found duplicate UUID ", uuid, " for components ", util::type_name(type1), " and ", util::type_name(type2)), std::move(stacktrace))
, uuid_(uuid) , uuid_(uuid)
, type1_(type1) , type1_(type1)

View file

@ -13,7 +13,7 @@ namespace psemek::ecs
struct component_not_found_exception struct component_not_found_exception
: util::exception : util::exception
{ {
component_not_found_exception(std::type_info const & type, handle const & handle, std::string const & description, boost::stacktrace::stacktrace stacktrace = {}) component_not_found_exception(std::type_info const & type, handle const & handle, std::string const & description, util::stacktrace stacktrace = {})
: util::exception(util::to_string("Component ", util::type_name(type), " not found for entity ", handle, " <", description, ">"), std::move(stacktrace)) : util::exception(util::to_string("Component ", util::type_name(type), " not found for entity ", handle, " <", description, ">"), std::move(stacktrace))
, type_(type) , type_(type)
, handle_(handle) , handle_(handle)
@ -37,7 +37,7 @@ namespace psemek::ecs
struct entity_not_cloneable struct entity_not_cloneable
: util::exception : util::exception
{ {
entity_not_cloneable(handle const & entity, std::vector<std::type_index> non_copyable_components, boost::stacktrace::stacktrace stacktrace = {}) entity_not_cloneable(handle const & entity, std::vector<std::type_index> non_copyable_components, util::stacktrace stacktrace = {})
: util::exception(make_message(entity, non_copyable_components), std::move(stacktrace)) : util::exception(make_message(entity, non_copyable_components), std::move(stacktrace))
, entity_(entity) , entity_(entity)
, non_copyable_components_(std::move(non_copyable_components)) , non_copyable_components_(std::move(non_copyable_components))

View file

@ -14,13 +14,13 @@ namespace psemek::io
struct null_istream struct null_istream
: null_stream : null_stream
{ {
null_istream(boost::stacktrace::stacktrace stacktrace = {}); null_istream(util::stacktrace stacktrace = {});
}; };
struct null_ostream struct null_ostream
: null_stream : null_stream
{ {
null_ostream(boost::stacktrace::stacktrace stacktrace = {}); null_ostream(util::stacktrace stacktrace = {});
}; };
struct stream_end struct stream_end
@ -32,13 +32,13 @@ namespace psemek::io
struct istream_end struct istream_end
: stream_end : stream_end
{ {
istream_end(boost::stacktrace::stacktrace stacktrace = {}); istream_end(util::stacktrace stacktrace = {});
}; };
struct ostream_end struct ostream_end
: stream_end : stream_end
{ {
ostream_end(boost::stacktrace::stacktrace stacktrace = {}); ostream_end(util::stacktrace stacktrace = {});
}; };
} }

View file

@ -3,19 +3,19 @@
namespace psemek::io namespace psemek::io
{ {
null_istream::null_istream(boost::stacktrace::stacktrace stacktrace) null_istream::null_istream(util::stacktrace stacktrace)
: null_stream("Attempt to read from null input stream", std::move(stacktrace)) : null_stream("Attempt to read from null input stream", std::move(stacktrace))
{} {}
null_ostream::null_ostream(boost::stacktrace::stacktrace stacktrace) null_ostream::null_ostream(util::stacktrace stacktrace)
: null_stream("Attempt to write to null output stream", std::move(stacktrace)) : null_stream("Attempt to write to null output stream", std::move(stacktrace))
{} {}
istream_end::istream_end(boost::stacktrace::stacktrace stacktrace) istream_end::istream_end(util::stacktrace stacktrace)
: stream_end("Unexpected input stream end", std::move(stacktrace)) : stream_end("Unexpected input stream end", std::move(stacktrace))
{} {}
ostream_end::ostream_end(boost::stacktrace::stacktrace stacktrace) ostream_end::ostream_end(util::stacktrace stacktrace)
: stream_end("Unexpected output stream end", std::move(stacktrace)) : stream_end("Unexpected output stream end", std::move(stacktrace))
{} {}

View file

@ -61,7 +61,7 @@ namespace psemek::log
if (p.signal == signal) if (p.signal == signal)
{ {
log::error() << p.message; log::error() << p.message;
log::error() << boost::stacktrace::stacktrace(); log::error() << util::stacktrace();
break; break;
} }
} }

View file

@ -8,7 +8,7 @@ namespace psemek::math::detail
struct empty_array_exception struct empty_array_exception
: util::exception : util::exception
{ {
empty_array_exception(boost::stacktrace::stacktrace stacktrace = {}) empty_array_exception(util::stacktrace stacktrace = {})
: util::exception("Indexing an empty array", std::move(stacktrace)) : util::exception("Indexing an empty array", std::move(stacktrace))
{} {}
}; };

View file

@ -14,25 +14,25 @@ namespace psemek::ml
struct empty_neural_net_error struct empty_neural_net_error
: neural_net_error : neural_net_error
{ {
empty_neural_net_error(boost::stacktrace::stacktrace stacktrace = {}); empty_neural_net_error(util::stacktrace stacktrace = {});
}; };
struct wrong_activation_types_count_error struct wrong_activation_types_count_error
: neural_net_error : neural_net_error
{ {
wrong_activation_types_count_error(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); wrong_activation_types_count_error(std::size_t expected, std::size_t actual, util::stacktrace stacktrace = {});
}; };
struct wrong_neural_net_input_size struct wrong_neural_net_input_size
: neural_net_error : neural_net_error
{ {
wrong_neural_net_input_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); wrong_neural_net_input_size(std::size_t expected, std::size_t actual, util::stacktrace stacktrace = {});
}; };
struct wrong_neural_net_output_size struct wrong_neural_net_output_size
: neural_net_error : neural_net_error
{ {
wrong_neural_net_output_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); wrong_neural_net_output_size(std::size_t expected, std::size_t actual, util::stacktrace stacktrace = {});
}; };
} }

View file

@ -5,19 +5,19 @@
namespace psemek::ml namespace psemek::ml
{ {
empty_neural_net_error::empty_neural_net_error(boost::stacktrace::stacktrace stacktrace) empty_neural_net_error::empty_neural_net_error(util::stacktrace stacktrace)
: neural_net_error("Neural net must have at least a single layer", std::move(stacktrace)) : neural_net_error("Neural net must have at least a single layer", std::move(stacktrace))
{} {}
wrong_activation_types_count_error::wrong_activation_types_count_error(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace) wrong_activation_types_count_error::wrong_activation_types_count_error(std::size_t expected, std::size_t actual, util::stacktrace stacktrace)
: neural_net_error(util::to_string("Wrong number of activation types: expected ", expected, ", got ", actual), std::move(stacktrace)) : neural_net_error(util::to_string("Wrong number of activation types: expected ", expected, ", got ", actual), std::move(stacktrace))
{} {}
wrong_neural_net_input_size::wrong_neural_net_input_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace) wrong_neural_net_input_size::wrong_neural_net_input_size(std::size_t expected, std::size_t actual, util::stacktrace stacktrace)
: neural_net_error(util::to_string("Wrong neural net input size: expected ", expected, ", got ", actual), std::move(stacktrace)) : neural_net_error(util::to_string("Wrong neural net input size: expected ", expected, ", got ", actual), std::move(stacktrace))
{} {}
wrong_neural_net_output_size::wrong_neural_net_output_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace) wrong_neural_net_output_size::wrong_neural_net_output_size(std::size_t expected, std::size_t actual, util::stacktrace stacktrace)
: neural_net_error(util::to_string("Wrong neural net output size: expected ", expected, ", got ", actual), std::move(stacktrace)) : neural_net_error(util::to_string("Wrong neural net output size: expected ", expected, ", got ", actual), std::move(stacktrace))
{} {}

View file

@ -16,7 +16,7 @@ namespace psemek::parser
struct parse_error struct parse_error
: util::exception : util::exception
{ {
parse_error(std::string message, std::size_t line, std::size_t character, boost::stacktrace::stacktrace stacktrace = {}) parse_error(std::string message, std::size_t line, std::size_t character, util::stacktrace stacktrace = {})
: util::exception(util::to_string(message, " at ", line, "#", character), std::move(stacktrace)) : util::exception(util::to_string(message, " at ", line, "#", character), std::move(stacktrace))
, message_{std::move(message)} , message_{std::move(message)}
, line_{line} , line_{line}

View file

@ -11,7 +11,7 @@ namespace psemek::rs
struct unknown_id_error struct unknown_id_error
: util::exception : util::exception
{ {
unknown_id_error(rs::id id, boost::stacktrace::stacktrace stacktrace = {}); unknown_id_error(rs::id id, util::stacktrace stacktrace = {});
rs::id id() const { return id_; } rs::id id() const { return id_; }
@ -22,7 +22,7 @@ namespace psemek::rs
struct unknown_name_error struct unknown_name_error
: util::exception : util::exception
{ {
unknown_name_error(std::string_view name, boost::stacktrace::stacktrace stacktrace = {}); unknown_name_error(std::string_view name, util::stacktrace stacktrace = {});
std::string_view name() const { return name_; } std::string_view name() const { return name_; }

View file

@ -41,12 +41,12 @@ namespace psemek::rs
} }
unknown_id_error::unknown_id_error(rs::id id, boost::stacktrace::stacktrace stacktrace) unknown_id_error::unknown_id_error(rs::id id, util::stacktrace stacktrace)
: util::exception(util::to_string("unknown resource id: ", id), std::move(stacktrace)) : util::exception(util::to_string("unknown resource id: ", id), std::move(stacktrace))
, id_(id) , id_(id)
{} {}
unknown_name_error::unknown_name_error(std::string_view name, boost::stacktrace::stacktrace stacktrace) unknown_name_error::unknown_name_error(std::string_view name, util::stacktrace stacktrace)
: util::exception(util::to_string("unknown resource name: ", name), std::move(stacktrace)) : util::exception(util::to_string("unknown resource name: ", name), std::move(stacktrace))
{} {}

View file

@ -5,8 +5,12 @@ file(GLOB_RECURSE PSEMEK_UTIL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "so
psemek_add_library(psemek-util ${PSEMEK_UTIL_HEADERS} ${PSEMEK_UTIL_SOURCES}) psemek_add_library(psemek-util ${PSEMEK_UTIL_HEADERS} ${PSEMEK_UTIL_SOURCES})
target_include_directories(psemek-util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") target_include_directories(psemek-util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(psemek-util PUBLIC ${CMAKE_THREAD_LIBS_INIT} Boost::boost libbacktrace) target_link_libraries(psemek-util PUBLIC ${CMAKE_THREAD_LIBS_INIT} Boost::boost)
target_compile_definitions(psemek-util PUBLIC "-DBOOST_STACKTRACE_USE_BACKTRACE")
if(PSEMEK_STACKTRACE)
target_link_libraries(psemek-util PUBLIC libbacktrace)
target_compile_definitions(psemek-util PUBLIC "-DBOOST_STACKTRACE_USE_BACKTRACE")
endif()
psemek_glob_tests(psemek-util tests) psemek_glob_tests(psemek-util tests)

View file

@ -12,7 +12,7 @@
namespace psemek::util namespace psemek::util
{ {
[[noreturn]] inline bool assertion_handler(char const * assertion, boost::stacktrace::stacktrace stacktrace = {}) [[noreturn]] inline bool assertion_handler(char const * assertion, stacktrace stacktrace = {})
{ {
throw ::psemek::util::exception(assertion, std::move(stacktrace)); throw ::psemek::util::exception(assertion, std::move(stacktrace));
} }

View file

@ -36,7 +36,7 @@ namespace psemek::util
struct key_error struct key_error
: key_error_base : key_error_base
{ {
key_error(Key const & key, boost::stacktrace::stacktrace stacktrace = {}) key_error(Key const & key, util::stacktrace stacktrace = {})
: key_error_base(detail::key_error_to_string(key), std::move(stacktrace)) : key_error_base(detail::key_error_to_string(key), std::move(stacktrace))
, key_(key) , key_(key)
{} {}

View file

@ -26,7 +26,7 @@ namespace psemek::util
struct unknown_enum_value_exception struct unknown_enum_value_exception
: unknown_enum_value_exception_base : unknown_enum_value_exception_base
{ {
unknown_enum_value_exception(Enum value, boost::stacktrace::stacktrace stacktrace = {}) unknown_enum_value_exception(Enum value, util::stacktrace stacktrace = {})
: unknown_enum_value_exception_base(to_string("unknown ", type_name<Enum>(), " value: ", static_cast<std::underlying_type_t<Enum>>(value)), std::move(stacktrace)) : unknown_enum_value_exception_base(to_string("unknown ", type_name<Enum>(), " value: ", static_cast<std::underlying_type_t<Enum>>(value)), std::move(stacktrace))
, value_(value) , value_(value)
{} {}

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <boost/stacktrace.hpp> #ifdef PSEMEK_STACKTRACE
#include <boost/stacktrace.hpp>
#endif
#include <exception> #include <exception>
#include <string> #include <string>
@ -9,10 +11,24 @@
namespace psemek::util namespace psemek::util
{ {
#ifdef PSEMEK_STACKTRACE
using stacktrace = boost::stacktrace::stacktrace;
#else
struct stacktrace
{
template <typename Stream>
friend Stream & operator << (Stream & stream, stacktrace)
{
stream << "(stacktrace disabled)\n";
return stream;
}
};
#endif
struct exception struct exception
: std::exception : std::exception
{ {
exception(std::string message, boost::stacktrace::stacktrace stacktrace = {}) exception(std::string message, stacktrace stacktrace = {})
: message_(std::move(message)) : message_(std::move(message))
, stacktrace_(std::move(stacktrace)) , stacktrace_(std::move(stacktrace))
{} {}
@ -27,14 +43,14 @@ namespace psemek::util
return message_; return message_;
} }
boost::stacktrace::stacktrace const & stacktrace() const noexcept auto const & stacktrace() const noexcept
{ {
return stacktrace_; return stacktrace_;
} }
private: private:
std::string message_; std::string message_;
boost::stacktrace::stacktrace stacktrace_; util::stacktrace stacktrace_;
}; };
inline std::ostream & operator << (std::ostream & os, exception const & e) inline std::ostream & operator << (std::ostream & os, exception const & e)

View file

@ -3,7 +3,6 @@
#include <psemek/util/exception.hpp> #include <psemek/util/exception.hpp>
#include <type_traits> #include <type_traits>
#include <memory>
#include <functional> #include <functional>
namespace psemek::util namespace psemek::util
@ -12,7 +11,7 @@ namespace psemek::util
struct empty_function_error struct empty_function_error
: exception : exception
{ {
empty_function_error(boost::stacktrace::stacktrace stacktrace = {}) empty_function_error(util::stacktrace stacktrace = {})
: exception("Trying to call an empty function", std::move(stacktrace)) : exception("Trying to call an empty function", std::move(stacktrace))
{} {}
}; };

View file

@ -8,7 +8,7 @@ namespace psemek::util
struct not_implemented_error struct not_implemented_error
: exception : exception
{ {
not_implemented_error(boost::stacktrace::stacktrace stacktrace = {}); not_implemented_error(util::stacktrace stacktrace = {});
}; };
[[noreturn]] void not_implemented(); [[noreturn]] void not_implemented();

View file

@ -8,7 +8,7 @@ namespace psemek::util
struct system_error struct system_error
: exception : exception
{ {
system_error(std::error_code error_code, std::string message, boost::stacktrace::stacktrace stacktrace = {}) system_error(std::error_code error_code, std::string message, util::stacktrace stacktrace = {})
: exception(message + ": " + error_code.message(), std::move(stacktrace)) : exception(message + ": " + error_code.message(), std::move(stacktrace))
, error_code_(error_code) , error_code_(error_code)
{} {}

View file

@ -73,7 +73,7 @@ namespace psemek::util
struct invalid_utf8 struct invalid_utf8
: exception : exception
{ {
invalid_utf8(char const * data, boost::stacktrace::stacktrace stacktrace = {}) invalid_utf8(char const * data, util::stacktrace stacktrace = {})
: exception("Invalid UTF-8 string", std::move(stacktrace)) : exception("Invalid UTF-8 string", std::move(stacktrace))
, data_{data} , data_{data}
{} {}

View file

@ -1,11 +1,9 @@
#include <psemek/util/not_implemented.hpp> #include <psemek/util/not_implemented.hpp>
#include <stdexcept>
namespace psemek::util namespace psemek::util
{ {
not_implemented_error::not_implemented_error(boost::stacktrace::stacktrace stacktrace) not_implemented_error::not_implemented_error(util::stacktrace stacktrace)
: exception("Not implemented", std::move(stacktrace)) : exception("Not implemented", std::move(stacktrace))
{} {}