diff --git a/libs/async/include/psemek/async/future.hpp b/libs/async/include/psemek/async/future.hpp index 7a281526..d3b588c9 100644 --- a/libs/async/include/psemek/async/future.hpp +++ b/libs/async/include/psemek/async/future.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -60,15 +61,19 @@ namespace psemek::async } struct empty_future_error - : std::exception + : util::exception { - char const * what() const noexcept { return "future is empty"; } + empty_future_error(boost::stacktrace::stacktrace stacktrace = {}) + : util::exception("Future is empty", std::move(stacktrace)) + {} }; struct canceled_task_error - : std::exception + : util::exception { - char const * what() const noexcept { return "task canceled"; } + canceled_task_error(boost::stacktrace::stacktrace stacktrace = {}) + : util::exception("Task canceled", std::move(stacktrace)) + {} }; struct auto_cancel_tag{}; @@ -177,15 +182,19 @@ namespace psemek::async }; struct empty_promise_error - : std::exception + : util::exception { - char const * what() const noexcept { return "promise is empty"; } + empty_promise_error(boost::stacktrace::stacktrace stacktrace = {}) + : util::exception("Promise is empty", std::move(stacktrace)) + {} }; struct satisfied_promise_error - : std::exception + : util::exception { - char const * what() const noexcept { return "promise already contains a value or exception"; } + satisfied_promise_error(boost::stacktrace::stacktrace stacktrace = {}) + : util::exception("Promise already contains a value or exception", std::move(stacktrace)) + {} }; template diff --git a/libs/async/source/synchronous_executor.cpp b/libs/async/source/synchronous_executor.cpp index 45eadb09..57179ce3 100644 --- a/libs/async/source/synchronous_executor.cpp +++ b/libs/async/source/synchronous_executor.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -12,7 +13,7 @@ namespace psemek::async void synchronous_executor::post_at(clock::time_point, task) { - throw std::runtime_error("synchronous_executor doesn't support executor::post_at"); + throw util::exception("Synchronous_executor doesn't support executor::post_at"); } void synchronous_executor::stop() diff --git a/libs/audio/source/recorder.cpp b/libs/audio/source/recorder.cpp index fa9ef060..b60d5fc1 100644 --- a/libs/audio/source/recorder.cpp +++ b/libs/audio/source/recorder.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -78,7 +79,7 @@ namespace psemek::audio std::shared_ptr record(stream_ptr stream) { if (!stream->length()) - throw std::runtime_error("cannot record an infinite stream"); + throw util::exception("Cannot record an infinite stream"); return record(stream, *stream->length()); } diff --git a/libs/audio/source/track_raw.cpp b/libs/audio/source/track_raw.cpp index eb27d525..713963e2 100644 --- a/libs/audio/source/track_raw.cpp +++ b/libs/audio/source/track_raw.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -82,14 +83,14 @@ namespace psemek::audio track_ptr load_raw(util::span samples) { if ((samples.size() % 2) != 0) - throw std::runtime_error("bad sample count"); + throw util::exception("Sample count must be even"); return std::make_shared(std::make_shared(samples)); } track_ptr load_raw(std::vector samples) { if ((samples.size() % 2) != 0) - throw std::runtime_error("bad sample count"); + throw util::exception("Sample count must be even"); return std::make_shared(std::make_shared(std::move(samples))); } diff --git a/libs/audio/source/track_wav.cpp b/libs/audio/source/track_wav.cpp index d3419cae..2177d597 100644 --- a/libs/audio/source/track_wav.cpp +++ b/libs/audio/source/track_wav.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #ifdef __GNUC__ #pragma GCC diagnostic push @@ -24,7 +25,7 @@ namespace psemek::audio std::vector convert_audio(std::vector> const & channels, int frequency) { if (channels.empty() || channels.size() > 2) - throw std::runtime_error(util::to_string("Can't convert audio with ", static_cast(channels.size()), " channels")); + throw util::exception(util::to_string("Can't convert audio with ", static_cast(channels.size()), " channels")); std::vector result(channels[0].size() * 2); auto out = result.begin(); @@ -66,7 +67,7 @@ namespace psemek::audio AudioFile audio_file; audio_file.shouldLogErrorsToConsole(false); audio_file.onError = [](std::string const & error) { - throw std::runtime_error("failed to load WAV file: " + error); + throw util::exception("Failed to load WAV file: " + error); }; audio_file.loadFromMemory(data_u8); diff --git a/libs/fonts/source/bmfont.cpp b/libs/fonts/source/bmfont.cpp index 113fcb07..cab715c7 100644 --- a/libs/fonts/source/bmfont.cpp +++ b/libs/fonts/source/bmfont.cpp @@ -1,9 +1,10 @@ #include #include +#include #include -#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::std::runtime_error("Error parsing font description: " BOOST_PP_STRINGIZE(x)); +#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::psemek::util::exception("Error parsing font description: " BOOST_PP_STRINGIZE(x)); #define RAPIDJSON_NOEXCEPT_ASSERT(x) #include @@ -53,7 +54,7 @@ namespace psemek::fonts document.ParseInsitu(description_str.data()); if (document.HasParseError()) - throw std::runtime_error(util::to_string("Error msdf font description: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset())); + throw util::exception(util::to_string("Error msdf font description: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset())); bmfont_data result; diff --git a/libs/fonts/source/kerned_font.cpp b/libs/fonts/source/kerned_font.cpp index 8ac1bab0..3c02d66a 100644 --- a/libs/fonts/source/kerned_font.cpp +++ b/libs/fonts/source/kerned_font.cpp @@ -1,6 +1,7 @@ #include #include +#include namespace psemek::fonts { @@ -10,9 +11,9 @@ namespace psemek::fonts , atlas_{std::move(atlas)} { if (!supports_character('?')) - throw std::runtime_error("Kerned font must support '?' character"); + throw util::exception("Kerned font must support '?' character"); if (!supports_character(' ')) - throw std::runtime_error("Kerned font must support ' ' character"); + throw util::exception("Kerned font must support ' ' character"); std::vector chars; for (auto const & g : data_.glyphs) diff --git a/libs/fonts/source/monospace_font.cpp b/libs/fonts/source/monospace_font.cpp index 6ffee890..6341042c 100644 --- a/libs/fonts/source/monospace_font.cpp +++ b/libs/fonts/source/monospace_font.cpp @@ -1,6 +1,7 @@ #include #include +#include namespace psemek::fonts { @@ -13,10 +14,10 @@ namespace psemek::fonts , texcoords_{std::move(texcoords)} { if (range_.end - range_.begin != texcoords_.size()) - throw std::runtime_error("Wrong number of texture coordinates"); + throw util::exception("Wrong number of texture coordinates"); if (!supports_character('?')) - throw std::runtime_error("Monospace font must support '?' character"); + throw util::exception("Monospace font must support '?' character"); } static geom::vector advance_dir(shape_options::direction_t direction) diff --git a/libs/geom/include/psemek/geom/bezier.hpp b/libs/geom/include/psemek/geom/bezier.hpp index 57f9a5f3..3c78feec 100644 --- a/libs/geom/include/psemek/geom/bezier.hpp +++ b/libs/geom/include/psemek/geom/bezier.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -30,7 +31,7 @@ namespace psemek::geom : points_(std::move(points)) { if (points_.empty()) - throw std::runtime_error("Points array should be non-empty"); + throw util::exception("Points array should be non-empty"); temp_.resize(points_.size()); } diff --git a/libs/geom/include/psemek/geom/detail/array.hpp b/libs/geom/include/psemek/geom/detail/array.hpp index 3e451546..b7594549 100644 --- a/libs/geom/include/psemek/geom/detail/array.hpp +++ b/libs/geom/include/psemek/geom/detail/array.hpp @@ -1,17 +1,16 @@ #pragma once -#include +#include namespace psemek::geom::detail { struct empty_array_exception - : std::exception + : util::exception { - char const * what() const noexcept - { - return "Attempt to index a zero vector"; - } + empty_array_exception(boost::stacktrace::stacktrace stacktrace = {}) + : util::exception("Indexing an empty array", std::move(stacktrace)) + {} }; template diff --git a/libs/gfx/include/psemek/gfx/gltf_animation.hpp b/libs/gfx/include/psemek/gfx/gltf_animation.hpp index 858f21ee..711c9e93 100644 --- a/libs/gfx/include/psemek/gfx/gltf_animation.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_animation.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -149,7 +150,7 @@ namespace psemek::gfx ); } default: - throw std::runtime_error("unsupported glTF animation interpolation type"); + throw util::exception("Unknown glTF animation interpolation type"); } } diff --git a/libs/gfx/source/dither.cpp b/libs/gfx/source/dither.cpp index 30681233..f57050ba 100644 --- a/libs/gfx/source/dither.cpp +++ b/libs/gfx/source/dither.cpp @@ -1,4 +1,5 @@ #include +#include namespace psemek::gfx { @@ -25,7 +26,7 @@ namespace psemek::gfx basic_pixmap ordered_dither(std::size_t size) { if (size > 16) - throw std::runtime_error("8-bit dither map cannot be larger than 16x16"); + throw util::exception("8-bit dither map cannot be larger than 16x16"); basic_pixmap result({size, size}); diff --git a/libs/gfx/source/error.cpp b/libs/gfx/source/error.cpp index fa9a7c34..dc45b59f 100644 --- a/libs/gfx/source/error.cpp +++ b/libs/gfx/source/error.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -31,8 +32,8 @@ namespace psemek::gfx auto e = gl::GetError(); - if (e != gl::GetError()) - throw std::runtime_error(util::to_string(context, gl_error_str(e))); + if (e != gl::NO_ERROR) + throw util::exception(util::to_string(context, gl_error_str(e))); } } diff --git a/libs/gfx/source/framebuffer.cpp b/libs/gfx/source/framebuffer.cpp index 93bf87f1..d3cb3f4c 100644 --- a/libs/gfx/source/framebuffer.cpp +++ b/libs/gfx/source/framebuffer.cpp @@ -1,6 +1,7 @@ #include #include +#include namespace psemek::gfx { @@ -209,7 +210,7 @@ namespace psemek::gfx void framebuffer::assert_complete(std::string_view name) const { if (auto s = status(); s != gl::FRAMEBUFFER_COMPLETE) - throw std::runtime_error(util::to_string("Framebuffer ", name, name.empty() ? "" : " ", "incomplete: ", framebuffer_status_string(s))); + throw util::exception(util::to_string("Framebuffer ", name, name.empty() ? "" : " ", "incomplete: ", framebuffer_status_string(s))); } framebuffer::framebuffer(std::nullptr_t) diff --git a/libs/gfx/source/gltf_parser.cpp b/libs/gfx/source/gltf_parser.cpp index 7c3f10a2..e31491a0 100644 --- a/libs/gfx/source/gltf_parser.cpp +++ b/libs/gfx/source/gltf_parser.cpp @@ -3,11 +3,12 @@ #include #include #include +#include #include #include -#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::std::runtime_error("Error parsing glTF: " BOOST_PP_STRINGIZE(x)); +#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::psemek::util::exception("Error parsing glTF: " BOOST_PP_STRINGIZE(x)); #define RAPIDJSON_NOEXCEPT_ASSERT(x) #include @@ -65,7 +66,7 @@ namespace psemek::gfx return gltf_asset::accessor::mat3; if (type == "MAT4") return gltf_asset::accessor::mat4; - throw std::runtime_error(util::to_string("Unknown accessor component type: ", type)); + throw util::exception(util::to_string("Unknown accessor component type: ", type)); } static std::unordered_set supported_extensions = @@ -126,14 +127,14 @@ namespace psemek::gfx document.ParseInsitu(description_str.data()); if (document.HasParseError()) - throw std::runtime_error(util::to_string("Error parsing glTF: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset())); + throw util::exception(util::to_string("Error parsing glTF: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset())); gltf_asset result; if (document.HasMember("extensionsRequired")) for (auto const & extension : document["extensionsRequired"].GetArray()) if (!supported_extensions.contains(extension.GetString())) - throw std::runtime_error("glTF extension " + std::string(extension.GetString()) + " is not supported"); + throw util::exception("glTF extension " + std::string(extension.GetString()) + " is not supported"); if (document.HasMember("nodes")) for (auto const & node : document["nodes"].GetArray()) { @@ -323,7 +324,7 @@ namespace psemek::gfx else if (path == "translation") channel_target.path = gltf_asset::animation::channel::translation; else - throw std::runtime_error("Unknown glTF animation target path: " + path); + throw util::exception("Unknown glTF animation target path: " + path); } for (auto const & sampler : animation["samplers"].GetArray()) @@ -341,7 +342,7 @@ namespace psemek::gfx else if (interpolation == "CUBICSPLINE") sampler_target.interpolation = geom::easing_type::cubic; else - throw std::runtime_error("Unknown glTF animation interpolation type: " + interpolation); + throw util::exception("Unknown glTF animation interpolation type: " + interpolation); } } @@ -401,7 +402,7 @@ namespace psemek::gfx else if (type == "spot") target.type = gltf_asset::light::spot; else - throw std::runtime_error("unknown light type: " + type); + throw util::exception("Unknown light type: " + type); if (light.HasMember("color")) { @@ -459,7 +460,7 @@ namespace psemek::gfx case type_t::vec2: return 2; case type_t::vec3: return 3; case type_t::vec4: return 4; - default: throw std::runtime_error("unsupported attribute type"); + default: throw util::exception("Unsupported attribute type"); } } diff --git a/libs/gfx/source/init.cpp b/libs/gfx/source/init.cpp index b0a72cc1..2b10eeb6 100644 --- a/libs/gfx/source/init.cpp +++ b/libs/gfx/source/init.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace psemek::gfx { @@ -8,7 +9,7 @@ namespace psemek::gfx void init() { if (!gl::sys::initialize()) - throw std::runtime_error("Failed to load OpenGL functions"); + throw util::exception("Failed to load OpenGL functions"); auto vendor = gl::GetString(gl::VENDOR); auto renderer = gl::GetString(gl::RENDERER); diff --git a/libs/gfx/source/mesh.cpp b/libs/gfx/source/mesh.cpp index 19897e45..faafe4b0 100644 --- a/libs/gfx/source/mesh.cpp +++ b/libs/gfx/source/mesh.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::gfx { @@ -127,7 +128,7 @@ namespace psemek::gfx case gl::UNSIGNED_BYTE: return 1; case gl::UNSIGNED_SHORT: return 2; case gl::UNSIGNED_INT: return 4; - default: throw std::runtime_error("Unknown undex type"); + default: throw util::exception("Unknown undex type"); } } @@ -291,7 +292,7 @@ namespace psemek::gfx auto pose_count = s.read(); if (pose_count != result.bones.size()) - throw std::runtime_error("Number of transforms in a pose must be equal to the number of bones"); + throw util::exception("Number of transforms in a pose must be equal to the number of bones"); auto pose_ptr = s.read_ptr>(pose_count); std::string_view name(name_ptr, name_length); @@ -310,32 +311,32 @@ namespace psemek::gfx { case SECTION_MESH: if (had_section_mesh) - throw std::runtime_error("Section 'mesh' must not repeat"); + throw util::exception("Section 'mesh' must not repeat"); parse_section_mesh(); had_section_mesh = true; break; case SECTION_BONES: if (had_section_bones) - throw std::runtime_error("Section 'bones' must not repeat"); + throw util::exception("Section 'bones' must not repeat"); if (!had_section_mesh) - throw std::runtime_error("Section 'bones' must come after section 'mesh'"); + throw util::exception("Section 'bones' must come after section 'mesh'"); if ((vertex_format & WEIGHTS_MASK) == 0) - throw std::runtime_error("Section 'bones' requires weights in vertex format"); + throw util::exception("Section 'bones' requires weights in vertex format"); parse_section_bones(); had_section_bones = true; break; case SECTION_POSE: if (!had_section_bones) - throw std::runtime_error("Section 'pose' must come after section 'bones'"); + throw util::exception("Section 'pose' must come after section 'bones'"); parse_section_pose(); break; default: - throw std::runtime_error("Unknown section code " + util::to_string(section_type)); + throw util::exception("Unknown section code " + util::to_string(section_type)); } } if (!had_section_mesh) - throw std::runtime_error("Section 'mesh' must be present"); + throw util::exception("Section 'mesh' must be present"); return result; } diff --git a/libs/gfx/source/netpbm.cpp b/libs/gfx/source/netpbm.cpp index 48a033f1..f8d401c4 100644 --- a/libs/gfx/source/netpbm.cpp +++ b/libs/gfx/source/netpbm.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,7 +12,7 @@ namespace psemek::gfx { auto fail = [](std::string str) { - throw std::runtime_error("Error loading PBM image: " + str); + throw util::exception("Error loading PBM image: " + str); }; std::string line; @@ -73,7 +74,7 @@ namespace psemek::gfx { auto fail = [](std::string str) { - throw std::runtime_error("Error loading PGM image: " + str); + throw util::exception("Error loading PGM image: " + str); }; std::string line; @@ -134,7 +135,7 @@ namespace psemek::gfx { auto fail = [](std::string str) { - throw std::runtime_error("Error loading PPM image: " + str); + throw util::exception("Error loading PPM image: " + str); }; std::string line; diff --git a/libs/gfx/source/obj_parser.cpp b/libs/gfx/source/obj_parser.cpp index 87b8a7f2..7054946e 100644 --- a/libs/gfx/source/obj_parser.cpp +++ b/libs/gfx/source/obj_parser.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -20,7 +21,7 @@ namespace psemek::gfx std::size_t line_count = 0; auto fail = [&](auto const & ... args){ - throw std::runtime_error(util::to_string("Error parsing OBJ data, line ", line_count, ": ", args...)); + throw util::exception(util::to_string("Error parsing OBJ data, line ", line_count, ": ", args...)); }; while (std::getline(is >> std::ws, line)) diff --git a/libs/gfx/source/painter.cpp b/libs/gfx/source/painter.cpp index a43adec0..bb1420fe 100644 --- a/libs/gfx/source/painter.cpp +++ b/libs/gfx/source/painter.cpp @@ -5,6 +5,7 @@ #include #include #include +#include static const char vertex_source[] = R"( @@ -290,7 +291,7 @@ namespace psemek::gfx s = {9.f, 12.f}; break; default: - throw std::runtime_error("Unknown font"); + throw util::unknown_enum_value_exception(f); } s[0] *= str.size() * scale; @@ -485,7 +486,7 @@ namespace psemek::gfx pen[0] -= size[0]; break; default: - throw std::runtime_error("Unknown x alignment"); + throw util::unknown_enum_value_exception(opts.x); } switch (opts.y) @@ -499,7 +500,7 @@ namespace psemek::gfx pen[1] -= size[1]; break; default: - throw std::runtime_error("Unknown y alignment"); + throw util::unknown_enum_value_exception(opts.y); } geom::vector const sx = {9.f * opts.scale, 0.f, 0.f}; diff --git a/libs/gfx/source/png.cpp b/libs/gfx/source/png.cpp index 979e1623..f3e77691 100644 --- a/libs/gfx/source/png.cpp +++ b/libs/gfx/source/png.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -16,7 +17,7 @@ namespace psemek::gfx { png_error_ptr error = [](png_structp, png_const_charp str) { - throw std::runtime_error(str); + throw util::exception(str); }; png_error_ptr warn = [](png_structp, png_const_charp str) @@ -25,7 +26,7 @@ namespace psemek::gfx }; auto png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, error, warn); - if (!png) throw std::runtime_error("png_create_read_struct returned null"); + if (!png) throw util::exception("png_create_read_struct returned null"); png_set_error_fn(png, nullptr, error, warn); @@ -37,10 +38,10 @@ namespace psemek::gfx }); info = png_create_info_struct(png); - if (!info) throw std::runtime_error("png_create_info_struct returned null"); + if (!info) throw util::exception("png_create_info_struct returned null"); end_info = png_create_info_struct(png); - if (!end_info) throw std::runtime_error("png_create_info_struct returned null"); + if (!end_info) throw util::exception("png_create_info_struct returned null"); png_rw_ptr read = [](png_structp png, png_bytep ptr, size_t count){ reinterpret_cast(png_get_io_ptr(png))->read(reinterpret_cast(ptr), count); @@ -58,7 +59,7 @@ namespace psemek::gfx png_set_strip_16(png); if (monochrome && color_type != PNG_COLOR_TYPE_GRAY) - throw std::runtime_error("invalid color type for monochrome PNG"); + throw util::exception("Invalid color type for monochrome PNG"); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); @@ -75,7 +76,7 @@ namespace psemek::gfx auto const row_bytes = png_get_rowbytes(png, info); if (row_bytes != width * sizeof(Pixel)) - throw std::runtime_error("PNG row bytes mismatch"); + throw util::exception("PNG row bytes mismatch"); for (std::uint32_t i = 0; i < height; ++i) png_read_row(png, reinterpret_cast(result.data() + i * width), nullptr); @@ -87,7 +88,7 @@ namespace psemek::gfx { png_error_ptr error = [](png_structp, png_const_charp str) { - throw std::runtime_error(str); + throw util::exception(str); }; png_error_ptr warn = [](png_structp, png_const_charp str) @@ -96,7 +97,7 @@ namespace psemek::gfx }; auto png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, error, warn); - if (!png) throw std::runtime_error("png_create_write_struct returned null"); + if (!png) throw util::exception("png_create_write_struct returned null"); png_set_error_fn(png, nullptr, error, warn); @@ -107,7 +108,7 @@ namespace psemek::gfx }); info = png_create_info_struct(png); - if (!info) throw std::runtime_error("png_create_info_struct returned null"); + if (!info) throw util::exception("png_create_info_struct returned null"); png_rw_ptr write = [](png_structp png, png_bytep ptr, size_t count){ reinterpret_cast(png_get_io_ptr(png))->write(reinterpret_cast(ptr), count); diff --git a/libs/gfx/source/program.cpp b/libs/gfx/source/program.cpp index b4bd4feb..61dd387a 100644 --- a/libs/gfx/source/program.cpp +++ b/libs/gfx/source/program.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -256,7 +257,7 @@ namespace psemek::gfx gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &log_len); std::unique_ptr log(new char [log_len]); gl::GetShaderInfoLog(shader, log_len, nullptr, log.get()); - throw std::runtime_error(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(source))); + throw util::exception(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(source))); } } @@ -274,7 +275,7 @@ namespace psemek::gfx gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &log_len); std::unique_ptr log(new char [log_len]); gl::GetProgramInfoLog(program, log_len, nullptr, log.get()); - throw std::runtime_error(util::to_string("Program link failed: ", log.get())); + throw util::exception(util::to_string("Program link failed: ", log.get())); } for (auto s : shaders) diff --git a/libs/gfx/source/renderer/deferred.cpp b/libs/gfx/source/renderer/deferred.cpp index 7716718f..27b1f4c8 100644 --- a/libs/gfx/source/renderer/deferred.cpp +++ b/libs/gfx/source/renderer/deferred.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -1079,13 +1080,13 @@ void main(){} assert(o.mesh); if (o.mat->lit && o.mat->transparent) - throw std::runtime_error("Materials that are both lit & transparent are not supported"); + throw util::exception("Materials that are both lit & transparent are not supported"); if (o.mat->lit && o.mat->blooming) - throw std::runtime_error("Materials that are both lit & blooming are not supported"); + throw util::exception("Materials that are both lit & blooming are not supported"); if (o.mat->casts_shadow && o.mat->transparent) - throw std::runtime_error("Transparent objects cannot cast shadow"); + throw util::exception("Transparent objects cannot cast shadow"); auto c = o.bbox.center() - all_bbox.corner(0, 0, 0); @@ -1567,9 +1568,9 @@ void main(){} if (l.shadowed) { if (l.cascades == 0) - throw std::runtime_error("The number of shadow map cascades should be positive"); + throw util::exception("The number of shadow map cascades should be positive"); if (l.cascades > 8) - throw std::runtime_error("More than 8 shadow map cascades are not supported"); + throw util::exception("More than 8 shadow map cascades are not supported"); light_transform.resize(l.cascades); diff --git a/libs/io/include/psemek/io/error.hpp b/libs/io/include/psemek/io/error.hpp index b531b75f..780fda8b 100644 --- a/libs/io/include/psemek/io/error.hpp +++ b/libs/io/include/psemek/io/error.hpp @@ -1,36 +1,44 @@ #pragma once -#include +#include namespace psemek::io { struct null_stream - : std::exception - {}; + : util::exception + { + using util::exception::exception; + }; struct null_istream : null_stream { - char const * what() const noexcept override; + null_istream(boost::stacktrace::stacktrace stacktrace = {}); }; struct null_ostream : null_stream { - char const * what() const noexcept override; + null_ostream(boost::stacktrace::stacktrace stacktrace = {}); + }; + + struct stream_end + : util::exception + { + using util::exception::exception; }; struct istream_end - : std::exception + : stream_end { - char const * what() const noexcept override; + istream_end(boost::stacktrace::stacktrace stacktrace = {}); }; struct ostream_end - : std::exception + : stream_end { - char const * what() const noexcept override; + ostream_end(boost::stacktrace::stacktrace stacktrace = {}); }; } diff --git a/libs/io/source/error.cpp b/libs/io/source/error.cpp index 2690fb13..681fcce6 100644 --- a/libs/io/source/error.cpp +++ b/libs/io/source/error.cpp @@ -3,24 +3,20 @@ namespace psemek::io { - char const * null_istream::what() const noexcept - { - return "Attempt to read from null input stream"; - } + null_istream::null_istream(boost::stacktrace::stacktrace stacktrace) + : null_stream("Attempt to read from null input stream", std::move(stacktrace)) + {} - char const * null_ostream::what() const noexcept - { - return "Attempt to write to null output stream"; - } + null_ostream::null_ostream(boost::stacktrace::stacktrace stacktrace) + : null_stream("Attempt to write to null output stream", std::move(stacktrace)) + {} - char const * istream_end::what() const noexcept - { - return "Unexpected input stream end"; - } + istream_end::istream_end(boost::stacktrace::stacktrace stacktrace) + : stream_end("Unexpected input stream end", std::move(stacktrace)) + {} - char const * ostream_end::what() const noexcept - { - return "Unexpected output stream end"; - } + ostream_end::ostream_end(boost::stacktrace::stacktrace stacktrace) + : stream_end("Unexpected output stream end", std::move(stacktrace)) + {} } diff --git a/libs/io/source/file_stream.cpp b/libs/io/source/file_stream.cpp index 56b3eb0e..0755c1ae 100644 --- a/libs/io/source/file_stream.cpp +++ b/libs/io/source/file_stream.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,7 +9,7 @@ namespace psemek::io static void throw_fopen [[noreturn]] (std::filesystem::path const & path) { - throw std::system_error(std::error_code{errno, std::system_category()}, "Failed to open " + path.string()); + throw util::system_error(std::error_code{errno, std::system_category()}, "Failed to open " + path.string()); } static FILE * safe_fopen(std::filesystem::path const & path, const char * mode) @@ -42,7 +43,7 @@ namespace psemek::io { case 0: return "wb"; case file_ostream::append: return "ab"; - default: throw std::runtime_error("Unknown file_ostream open flags"); + default: throw util::exception("Unknown file_ostream open flags"); } } diff --git a/libs/log/source/log.cpp b/libs/log/source/log.cpp index 3ff85ec4..3a213494 100644 --- a/libs/log/source/log.cpp +++ b/libs/log/source/log.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -153,7 +154,7 @@ namespace psemek::log auto it = thread_names.find(id); if (it != thread_names.end()) - throw std::runtime_error("Thread \"" + name + "\" already registered!"); + throw util::exception("Thread \"" + name + "\" already registered!"); thread_names[id] = name; } @@ -174,7 +175,7 @@ namespace psemek::log { std::ostringstream os; os << "Thread " << id << " not found!"; - throw std::runtime_error(os.str()); + throw util::exception(os.str()); } name = std::move(it->second); diff --git a/libs/ml/include/psemek/ml/neural_net/activation.hpp b/libs/ml/include/psemek/ml/neural_net/activation.hpp index e2ab6767..d5e7965b 100644 --- a/libs/ml/include/psemek/ml/neural_net/activation.hpp +++ b/libs/ml/include/psemek/ml/neural_net/activation.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -10,21 +12,12 @@ namespace psemek::ml // All activation functions are chosen in a way so that the derivative // can be expressed as a function of the activation function's value, i.e. // f'(x) = G(f(x)) for some G: R -> R - enum class activation_type - { - id, - sigmoid, - tanh, - relu, - - count, - }; - - struct unknown_activation_type - : std::exception - { - char const * what() const noexcept override; - }; + psemek_declare_enum(activation_type, std::uint8_t, + (id) + (sigmoid) + (tanh) + (relu) + ) template T activation(T x, activation_type type) @@ -39,7 +32,7 @@ namespace psemek::ml case activation_type::relu: return std::max(T{0}, x); default: - throw unknown_activation_type{}; + throw util::unknown_enum_value_exception{type}; } } @@ -56,7 +49,7 @@ namespace psemek::ml case activation_type::relu: return value == T{0} ? T{0} : T{1}; default: - throw unknown_activation_type{}; + throw util::unknown_enum_value_exception{type}; } } diff --git a/libs/ml/include/psemek/ml/neural_net/error.hpp b/libs/ml/include/psemek/ml/neural_net/error.hpp index 9274e1fb..b1731822 100644 --- a/libs/ml/include/psemek/ml/neural_net/error.hpp +++ b/libs/ml/include/psemek/ml/neural_net/error.hpp @@ -1,39 +1,38 @@ #pragma once -#include -#include +#include namespace psemek::ml { struct neural_net_error - : std::runtime_error + : util::exception { - using runtime_error::runtime_error; + using util::exception::exception; }; struct empty_neural_net_error : neural_net_error { - empty_neural_net_error(); + empty_neural_net_error(boost::stacktrace::stacktrace stacktrace = {}); }; struct wrong_activation_types_count_error : neural_net_error { - wrong_activation_types_count_error(std::size_t expected, std::size_t actual); + wrong_activation_types_count_error(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); }; struct wrong_neural_net_input_size : neural_net_error { - wrong_neural_net_input_size(std::size_t expected, std::size_t actual); + wrong_neural_net_input_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); }; struct wrong_neural_net_output_size : neural_net_error { - wrong_neural_net_output_size(std::size_t expected, std::size_t actual); + wrong_neural_net_output_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace = {}); }; } diff --git a/libs/ml/source/neural_net/activation.cpp b/libs/ml/source/neural_net/activation.cpp deleted file mode 100644 index 5fedabaf..00000000 --- a/libs/ml/source/neural_net/activation.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace psemek::ml -{ - - char const * unknown_activation_type::what() const noexcept - { - return "unknown activation type"; - } - -} diff --git a/libs/ml/source/neural_net/error.cpp b/libs/ml/source/neural_net/error.cpp index 0393a951..b338de66 100644 --- a/libs/ml/source/neural_net/error.cpp +++ b/libs/ml/source/neural_net/error.cpp @@ -5,20 +5,20 @@ namespace psemek::ml { - empty_neural_net_error::empty_neural_net_error() - : neural_net_error("neural net must have at least a single layer") + empty_neural_net_error::empty_neural_net_error(boost::stacktrace::stacktrace 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) - : neural_net_error(util::to_string("wrong number of activation types: expected ", expected, ", got ", actual)) + wrong_activation_types_count_error::wrong_activation_types_count_error(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace 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) - : neural_net_error(util::to_string("wrong neural net input size: expected ", expected, ", got ", actual)) + wrong_neural_net_input_size::wrong_neural_net_input_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace 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) - : neural_net_error(util::to_string("wrong neural net output size: expected ", expected, ", got ", actual)) + wrong_neural_net_output_size::wrong_neural_net_output_size(std::size_t expected, std::size_t actual, boost::stacktrace::stacktrace stacktrace) + : neural_net_error(util::to_string("Wrong neural net output size: expected ", expected, ", got ", actual), std::move(stacktrace)) {} } diff --git a/libs/ml/tests/neural_net/gradient.cpp b/libs/ml/tests/neural_net/gradient.cpp index a12e0c0f..3ff9c218 100644 --- a/libs/ml/tests/neural_net/gradient.cpp +++ b/libs/ml/tests/neural_net/gradient.cpp @@ -24,7 +24,7 @@ test_case(ml_neural__net_gradient) std::vector activations(sizes.size() - 1); for (auto & a : activations) - a = static_cast(uniform(rng, 0, static_cast(activation_type::count) - 1)); + a = static_cast(uniform(rng, 0, static_cast(activation_type_values().size()) - 1)); neural_net nn(std::move(sizes), std::move(activations)); randomize_normal(nn, rng); @@ -71,7 +71,7 @@ test_case(ml_neural__net_arg__gradient) std::vector activations(sizes.size() - 1); for (auto & a : activations) - a = static_cast(uniform(rng, 0, static_cast(activation_type::count) - 1)); + a = static_cast(uniform(rng, 0, static_cast(activation_type_values().size()) - 1)); neural_net nn(std::move(sizes), std::move(activations)); randomize_normal(nn, rng); diff --git a/libs/parser/include/psemek/parser/combinators.hpp b/libs/parser/include/psemek/parser/combinators.hpp index 5dc7cacc..180e61c6 100644 --- a/libs/parser/include/psemek/parser/combinators.hpp +++ b/libs/parser/include/psemek/parser/combinators.hpp @@ -199,7 +199,7 @@ namespace psemek::parser auto r = p.apply(buffer); if (r.index() == 1) break; if (buffer.it == pos) - throw grammar_error("infinite loop"); + throw grammar_error("Infinite loop"); res.push_back(std::move(std::get<0>(r))); } @@ -265,7 +265,7 @@ namespace psemek::parser if (res.index() == 1) return accum; if (pos == buffer.it) - throw grammar_error("infinite loop"); + throw grammar_error("Infinite loop"); accum = f(accum, std::get<0>(res)); } @@ -289,7 +289,7 @@ namespace psemek::parser if (res.index() == 1) return accum; if (pos == buffer.it) - throw grammar_error("infinite loop"); + throw grammar_error("Infinite loop"); accum = f(std::move(accum), std::move(std::get<0>(res))); } diff --git a/libs/parser/include/psemek/parser/parser.hpp b/libs/parser/include/psemek/parser/parser.hpp index 749d3390..441af71f 100644 --- a/libs/parser/include/psemek/parser/parser.hpp +++ b/libs/parser/include/psemek/parser/parser.hpp @@ -8,37 +8,35 @@ #include #include +#include namespace psemek::parser { struct parse_error - : std::runtime_error + : util::exception { - parse_error(std::string message, std::size_t line, std::size_t character) - : std::runtime_error(std::move(message)) + parse_error(std::string message, std::size_t line, std::size_t character, boost::stacktrace::stacktrace stacktrace = {}) + : util::exception(util::to_string(message, " at ", line, "#", character), std::move(stacktrace)) + , message_{std::move(message)} , line_{line} , character_{character} - , what_{util::to_string(message, " at ", line, "#", character)} {} + std::string const & message() const { return message_; } std::size_t line() const { return line_; } std::size_t character() const { return character_; } - const char * what() const noexcept { return what_.data(); } - private: + std::string message_; std::size_t line_; std::size_t character_; - std::string what_; }; struct grammar_error - : std::runtime_error + : util::exception { - grammar_error(std::string message) - : std::runtime_error(std::move(message)) - {} + using util::exception::exception; }; namespace detail diff --git a/libs/random/include/psemek/random/uniform.hpp b/libs/random/include/psemek/random/uniform.hpp index d125079e..ec277514 100644 --- a/libs/random/include/psemek/random/uniform.hpp +++ b/libs/random/include/psemek/random/uniform.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -65,7 +67,7 @@ namespace psemek::random using std::size; using std::begin; if (size(container) == 0) - throw std::runtime_error("cannot sample from empty container"); + throw util::exception("Cannot sample from empty container"); return *std::next(begin(container), uniform(rng, 0, size(container) - 1)); } diff --git a/libs/rs/include/psemek/rs/registry.hpp b/libs/rs/include/psemek/rs/registry.hpp index fb43390e..53360246 100644 --- a/libs/rs/include/psemek/rs/registry.hpp +++ b/libs/rs/include/psemek/rs/registry.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -8,9 +9,9 @@ namespace psemek::rs { struct unknown_id_error - : std::runtime_error + : util::exception { - unknown_id_error(rs::id id); + unknown_id_error(rs::id id, boost::stacktrace::stacktrace stacktrace = {}); rs::id id() const { return id_; } @@ -19,9 +20,9 @@ namespace psemek::rs }; struct unknown_name_error - : std::runtime_error + : util::exception { - unknown_name_error(std::string_view name); + unknown_name_error(std::string_view name, boost::stacktrace::stacktrace stacktrace = {}); std::string_view name() const { return name_; } diff --git a/libs/rs/source/registry.cpp b/libs/rs/source/registry.cpp index ab67b4d9..e37e0a60 100644 --- a/libs/rs/source/registry.cpp +++ b/libs/rs/source/registry.cpp @@ -42,13 +42,13 @@ namespace psemek::rs } - unknown_id_error::unknown_id_error(rs::id id) - : std::runtime_error(util::to_string("unknown resource id: ", id)) + unknown_id_error::unknown_id_error(rs::id id, boost::stacktrace::stacktrace stacktrace) + : util::exception(util::to_string("unknown resource id: ", id), std::move(stacktrace)) , id_(id) {} - unknown_name_error::unknown_name_error(std::string_view name) - : std::runtime_error(util::to_string("unknown resource name: ", name)) + unknown_name_error::unknown_name_error(std::string_view name, boost::stacktrace::stacktrace stacktrace) + : util::exception(util::to_string("unknown resource name: ", name), std::move(stacktrace)) {} resource const * find(id id) diff --git a/libs/sdl2/source/init.cpp b/libs/sdl2/source/init.cpp index 661f1664..8cfa47e1 100644 --- a/libs/sdl2/source/init.cpp +++ b/libs/sdl2/source/init.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -62,7 +63,7 @@ namespace psemek::sdl2 [[noreturn]] void fail(std::string const & message) { - throw std::runtime_error(message + SDL_GetError()); + throw util::exception(message + SDL_GetError()); } std::shared_ptr init(std::uint32_t subsystems) diff --git a/libs/sir/include/psemek/sir/memory.hpp b/libs/sir/include/psemek/sir/memory.hpp index 061d9ac4..8a50a749 100644 --- a/libs/sir/include/psemek/sir/memory.hpp +++ b/libs/sir/include/psemek/sir/memory.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace psemek::sir { @@ -27,7 +28,6 @@ namespace psemek::sir private: std::string_view data_; - std::size_t offset_; }; static_assert(is_istream_v); @@ -93,7 +93,7 @@ namespace psemek::sir T const & at(std::size_t i) const { if (i >= size()) - throw std::out_of_range("psemek::sir::vector::at"); + throw util::key_error(i); return begin_[i]; } @@ -263,7 +263,7 @@ namespace psemek::sir { auto p = find(key); if (p == end_) - throw std::out_of_range("psemek::sir::map::at"); + throw util::key_error(key); return p->second; } diff --git a/libs/ui/include/psemek/ui/impl/component_factory_base.hpp b/libs/ui/include/psemek/ui/impl/component_factory_base.hpp index e4aeefc1..1d6d24cc 100644 --- a/libs/ui/include/psemek/ui/impl/component_factory_base.hpp +++ b/libs/ui/include/psemek/ui/impl/component_factory_base.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -16,21 +17,20 @@ namespace psemek::ui::impl { struct component_not_supported_exception - : std::exception + : util::exception { - component_not_supported_exception(std::type_index type) - : type_(type) - , message_(util::to_string("UI component ", util::type_name(type_), " is not supported")) + component_not_supported_exception(std::type_index type, boost::stacktrace::stacktrace stacktrace = {}) + : util::exception(util::to_string("UI component ", util::type_name(type_), " is not supported"), std::move(stacktrace)) + , type_(type) {} - const char * what() const noexcept override + std::type_index type() const { - return message_.data(); + return type_; } private: std::type_index type_; - std::string message_; }; struct component_factory_base diff --git a/libs/util/include/psemek/util/assert.hpp b/libs/util/include/psemek/util/assert.hpp index 5feb9977..e42723cc 100644 --- a/libs/util/include/psemek/util/assert.hpp +++ b/libs/util/include/psemek/util/assert.hpp @@ -1,9 +1,10 @@ #pragma once +#include #undef assert #ifdef PSEMEK_DEBUG -#define assert(x) ((void)(!(x) && ::psemek::util::assert_handler(#x, __FILE__, __LINE__))) +#define assert(x) ((void)(!(x) && ::psemek::util::assertion_handler("Assertion failed: " #x))) #else #define assert(x) ((void)(x)) #endif @@ -11,6 +12,9 @@ namespace psemek::util { - [[noreturn]] bool assert_handler(char const * expression, char const * file, int line); + [[noreturn]] inline bool assertion_handler(char const * assertion, boost::stacktrace::stacktrace stacktrace = {}) + { + throw ::psemek::util::exception(assertion, std::move(stacktrace)); + } } diff --git a/libs/util/include/psemek/util/binary_stream.hpp b/libs/util/include/psemek/util/binary_stream.hpp index 93e41bf4..249ca268 100644 --- a/libs/util/include/psemek/util/binary_stream.hpp +++ b/libs/util/include/psemek/util/binary_stream.hpp @@ -1,8 +1,8 @@ #pragma once -#include -#include +#include +#include #include namespace psemek::util @@ -13,7 +13,7 @@ namespace psemek::util inline void unexpected_end() { - throw std::runtime_error("Unexpected binary stream end"); + throw exception("Unexpected binary stream end"); } template diff --git a/libs/util/include/psemek/util/ecs.hpp b/libs/util/include/psemek/util/ecs.hpp index b364783e..3886a5d1 100644 --- a/libs/util/include/psemek/util/ecs.hpp +++ b/libs/util/include/psemek/util/ecs.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -779,7 +780,7 @@ namespace psemek::util { auto p = species_[species.value]->get_species_component(); if (!p) - throw std::runtime_error(util::to_string("Component ", type_name(), " is not present in species ", species_[species.value]->name())); + throw util::exception(util::to_string("Component ", type_name(), " is not present in species ", species_[species.value]->name())); return *p; } @@ -795,7 +796,7 @@ namespace psemek::util auto u = ecs_detail::unpack(entity.value); auto p = species_[u.species]->get_entity_component(); if (!p) - throw std::runtime_error(util::to_string("Component ", type_name(), " is not present in species ", species_[u.species]->name())); + throw util::exception(util::to_string("Component ", type_name(), " is not present in species ", species_[u.species]->name())); return p[u.entity]; } diff --git a/libs/util/include/psemek/util/enum.hpp b/libs/util/include/psemek/util/enum.hpp index 76a4ae04..fba931d2 100644 --- a/libs/util/include/psemek/util/enum.hpp +++ b/libs/util/include/psemek/util/enum.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -16,17 +17,17 @@ namespace psemek::util { struct unknown_enum_value_exception_base - : std::runtime_error + : exception { - using std::runtime_error::runtime_error; + using exception::exception; }; template struct unknown_enum_value_exception : unknown_enum_value_exception_base { - unknown_enum_value_exception(Enum value) - : unknown_enum_value_exception_base(to_string("unknown ", type_name(), " value: ", static_cast>(value))) + unknown_enum_value_exception(Enum value, boost::stacktrace::stacktrace stacktrace = {}) + : unknown_enum_value_exception_base(to_string("unknown ", type_name(), " value: ", static_cast>(value)), std::move(stacktrace)) , value_(value) {} diff --git a/libs/util/include/psemek/util/function.hpp b/libs/util/include/psemek/util/function.hpp index d7679a2f..42d4cc6b 100644 --- a/libs/util/include/psemek/util/function.hpp +++ b/libs/util/include/psemek/util/function.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -7,6 +9,14 @@ namespace psemek::util { + struct empty_function_error + : exception + { + empty_function_error(boost::stacktrace::stacktrace stacktrace = {}) + : exception("Trying to call an empty function", std::move(stacktrace)) + {} + }; + template struct function; @@ -149,7 +159,7 @@ namespace psemek::util R function::operator()(Args1 && ... args) const { if (!vtable_) - throw std::bad_function_call(); + throw empty_function_error{}; return vtable_->call(const_cast(static_cast(&storage_)), std::forward(args)...); } diff --git a/libs/util/include/psemek/util/heterogeneous_container.hpp b/libs/util/include/psemek/util/heterogeneous_container.hpp index ba98de1b..166ccb75 100644 --- a/libs/util/include/psemek/util/heterogeneous_container.hpp +++ b/libs/util/include/psemek/util/heterogeneous_container.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -80,9 +81,10 @@ namespace psemek::util template struct tuple_apply_at_impl> { - static decltype(auto) apply(F && f, T & t, std::size_t) + static decltype(auto) apply(F && f, T & t, std::size_t i) { - throw std::out_of_range("Bad tuple index"); + throw key_error(i); + // For proper type deduction return std::forward(f)(std::get<0>(t)); } }; @@ -105,21 +107,13 @@ namespace psemek::util decltype(auto) tuple_apply_at(F && f, T & t, std::size_t i) { if (i >= std::tuple_size_v) - throw std::out_of_range("Bad tuple index"); + throw key_error(i); return tuple_apply_at_impl>>::apply(std::forward(f), t, i); } } - struct bad_handle - : std::out_of_range - { - bad_handle() - : std::out_of_range("Bad handle") - {} - }; - template struct heterogeneous_container { @@ -230,7 +224,7 @@ namespace psemek::util { auto u = unpack_handle(h); if (u.first >= types_count) - throw bad_handle{}; + throw exception("Broken handle"); detail::tuple_apply_at([&u](auto & t) -> void { t.erase(u.second); }, storage_, u.first); @@ -275,12 +269,12 @@ namespace psemek::util if constexpr (std::is_same_v, T>) { if (i != j) - throw std::bad_any_cast{}; + throw exception("Error in heterogeneous_container"); res = &t[hh]; } }, storage_); if (!res) - throw std::bad_any_cast{}; + throw exception("Error in heterogeneous_container"); return *res; } @@ -298,12 +292,12 @@ namespace psemek::util if constexpr (std::is_same_v, T>) { if (i != j) - throw std::bad_any_cast{}; + throw exception("Error in heterogeneous_container"); res = &t[hh]; } }, storage_); if (!res) - throw std::bad_any_cast{}; + throw exception("Error in heterogeneous_container"); return *res; } diff --git a/libs/util/include/psemek/util/not_implemented.hpp b/libs/util/include/psemek/util/not_implemented.hpp index 45dd01e0..420e035d 100644 --- a/libs/util/include/psemek/util/not_implemented.hpp +++ b/libs/util/include/psemek/util/not_implemented.hpp @@ -1,14 +1,14 @@ #pragma once -#include +#include namespace psemek::util { struct not_implemented_error - : std::exception + : exception { - char const * what() const noexcept override; + not_implemented_error(boost::stacktrace::stacktrace stacktrace = {}); }; [[noreturn]] void not_implemented(); diff --git a/libs/util/include/psemek/util/spatial_array.hpp b/libs/util/include/psemek/util/spatial_array.hpp index 5bd94a2e..1bec02b1 100644 --- a/libs/util/include/psemek/util/spatial_array.hpp +++ b/libs/util/include/psemek/util/spatial_array.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include @@ -112,7 +114,7 @@ namespace psemek::util for (std::size_t i = 0; i < N; ++i) { if (ix[i] < origin_[i] || ix[i] >= origin_[i] + static_cast(size[i])) - throw std::runtime_error("element out of range"); + throw exception(to_string("Spatial array index ", ix[i], "(#", i, ") is out of bounds [", origin_[i], origin_[i] + static_cast(size[i]), ")")); ix[i] -= origin_[i]; } return array_(ix); diff --git a/libs/util/include/psemek/util/system_error.hpp b/libs/util/include/psemek/util/system_error.hpp new file mode 100644 index 00000000..17f0f28a --- /dev/null +++ b/libs/util/include/psemek/util/system_error.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace psemek::util +{ + + struct system_error + : exception + { + system_error(std::error_code error_code, std::string message, boost::stacktrace::stacktrace stacktrace = {}) + : exception(message + ": " + error_code.message(), std::move(stacktrace)) + , error_code_(error_code) + {} + + std::error_code error_code() const + { + return error_code_; + } + + private: + std::error_code error_code_; + }; + +} diff --git a/libs/util/include/psemek/util/to_string.hpp b/libs/util/include/psemek/util/to_string.hpp index 103f05fc..2e8a1161 100644 --- a/libs/util/include/psemek/util/to_string.hpp +++ b/libs/util/include/psemek/util/to_string.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -42,7 +44,7 @@ namespace psemek::util T x; iss >> x; if (!iss) - throw std::invalid_argument("failed to parse from string"); + throw exception("Failed to parse from string"); return x; } diff --git a/libs/util/include/psemek/util/unicode.hpp b/libs/util/include/psemek/util/unicode.hpp index 2e7f807a..be388b55 100644 --- a/libs/util/include/psemek/util/unicode.hpp +++ b/libs/util/include/psemek/util/unicode.hpp @@ -1,8 +1,9 @@ #pragma once +#include + #include #include -#include namespace psemek::util { @@ -70,14 +71,13 @@ namespace psemek::util }; struct invalid_utf8 - : std::exception + : exception { - invalid_utf8(char const * data) - : data_{data} + invalid_utf8(char const * data, boost::stacktrace::stacktrace stacktrace = {}) + : exception("Invalid UTF-8 string", std::move(stacktrace)) + , data_{data} {} - char const * what() const noexcept override; - char const * data() const { return data_; } private: diff --git a/libs/util/source/assert.cpp b/libs/util/source/assert.cpp deleted file mode 100644 index 7950ab5a..00000000 --- a/libs/util/source/assert.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -#include - -namespace psemek::util -{ - - [[noreturn]] bool assert_handler(char const * expression, char const * file, int line) - { - throw std::runtime_error(to_string(file, ":", line, " Assertion failed: ", expression)); - } - -} diff --git a/libs/util/source/executable_path.cpp b/libs/util/source/executable_path.cpp index b4e92ff7..83415df8 100644 --- a/libs/util/source/executable_path.cpp +++ b/libs/util/source/executable_path.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifdef WIN32 #include @@ -23,13 +24,13 @@ namespace psemek::util else if (error == ERROR_INSUFFICIENT_BUFFER) result.resize(result.size() * 2); else - throw std::runtime_error(util::to_string("failed to retrieve executable path: ", std::hex, error)); + throw exception(util::to_string("failed to retrieve executable path: ", std::hex, error)); } return std::filesystem::path(result); #elif defined __linux__ return std::filesystem::canonical("/proc/self/exe"); #else - throw std::runtime_error("executable_path() is not implemented for this platform"); + throw exception("executable_path() is not implemented for this platform"); #endif } diff --git a/libs/util/source/function.cpp b/libs/util/source/function.cpp deleted file mode 100644 index b2c513fb..00000000 --- a/libs/util/source/function.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include - -namespace psemek::util -{ - - namespace detail - { - - void bad_function_call() - { - throw std::bad_function_call{}; - } - - } - -} diff --git a/libs/util/source/not_implemented.cpp b/libs/util/source/not_implemented.cpp index 70515579..5c3e6d83 100644 --- a/libs/util/source/not_implemented.cpp +++ b/libs/util/source/not_implemented.cpp @@ -5,10 +5,9 @@ namespace psemek::util { - char const * not_implemented_error::what() const noexcept - { - return "not implemented"; - } + not_implemented_error::not_implemented_error(boost::stacktrace::stacktrace stacktrace) + : exception("Not implemented", std::move(stacktrace)) + {} void not_implemented() { diff --git a/libs/util/source/unicode.cpp b/libs/util/source/unicode.cpp index 38005dba..2b44ec43 100644 --- a/libs/util/source/unicode.cpp +++ b/libs/util/source/unicode.cpp @@ -110,9 +110,4 @@ namespace psemek::util throw invalid_utf8(ptr); } - char const * invalid_utf8::what() const noexcept - { - return "Invalid UTF-8 string"; - } - } diff --git a/libs/util/tests/function.cpp b/libs/util/tests/function.cpp index 688feaff..cc54656a 100644 --- a/libs/util/tests/function.cpp +++ b/libs/util/tests/function.cpp @@ -11,7 +11,7 @@ using namespace psemek::util; test_case(util_function_empty_call) { function f; - expect_throw(f(), std::bad_function_call); + expect_throw(f(), empty_function_error); } using heavy = std::array; diff --git a/libs/util/tests/heterogeneous_container.cpp b/libs/util/tests/heterogeneous_container.cpp index 42f3983e..11b2255e 100644 --- a/libs/util/tests/heterogeneous_container.cpp +++ b/libs/util/tests/heterogeneous_container.cpp @@ -96,9 +96,9 @@ test_case(util_heterogeneous__container_get) expect_equal_deref(std::get<2>(c.get(f0)), 3.14f); expect_equal_deref(std::get(c.get(f0)), 3.14f); - expect_throw(c.get(c0), std::bad_any_cast); - expect_throw(c.get(c1), std::bad_any_cast); - expect_throw(c.get(f0), std::bad_any_cast); + expect_throw(c.get(c0), std::exception); + expect_throw(c.get(c1), std::exception); + expect_throw(c.get(f0), std::exception); } test_case(util_heterogeneous__container_visit) diff --git a/tools/test/include/psemek/test/test.hpp b/tools/test/include/psemek/test/test.hpp index 5d439d4f..0cbf3820 100644 --- a/tools/test/include/psemek/test/test.hpp +++ b/tools/test/include/psemek/test/test.hpp @@ -13,64 +13,70 @@ // Have to put it before including to_string.hpp due to how unqualified lookup works, // see e.g. https://alexanderlobov.net/posts/2019-07-08-function-lookup-in-templates -template -std::ostream & operator << (std::ostream & s, std::optional const & o) -{ - if (o) - s << *o; - else - s << "(empty)"; - return s; -} -template -std::ostream & operator << (std::ostream & s, std::pair const & p) +namespace std { - s << '(' << p.first << ", " << p.second << ')'; - return s; -} -template -std::ostream & operator << (std::ostream & s, std::tuple const & t) -{ - s << '('; - [&](std::index_sequence){ - ((s << (Is == 0 ? "" : ", ") << std::get(t)), ...); - }(std::make_index_sequence{}); - s << ')'; - return s; -} - -template -std::ostream & operator << (std::ostream & s, std::vector const & v) -{ - s << "["; - bool first = true; - for (auto const & x : v) + template + ostream & operator << (ostream & s, optional const & o) { - if (!first) - s << ", "; - first = false; - s << x; + if (o) + s << *o; + else + s << "(empty)"; + return s; } - s << "]"; - return s; -} -template -std::ostream & operator << (std::ostream & s, std::set const & v) -{ - s << "{"; - bool first = true; - for (auto const & x : v) + template + ostream & operator << (ostream & s, pair const & p) { - if (!first) - s << ", "; - first = false; - s << x; + s << '(' << p.first << ", " << p.second << ')'; + return s; } - s << "}"; - return s; + + template + ostream & operator << (ostream & s, tuple const & t) + { + s << '('; + [&](index_sequence){ + ((s << (Is == 0 ? "" : ", ") << get(t)), ...); + }(make_index_sequence{}); + s << ')'; + return s; + } + + template + ostream & operator << (ostream & s, vector const & v) + { + s << "["; + bool first = true; + for (auto const & x : v) + { + if (!first) + s << ", "; + first = false; + s << x; + } + s << "]"; + return s; + } + + template + ostream & operator << (ostream & s, set const & v) + { + s << "{"; + bool first = true; + for (auto const & x : v) + { + if (!first) + s << ", "; + first = false; + s << x; + } + s << "}"; + return s; + } + } namespace psemek::util @@ -81,16 +87,21 @@ namespace psemek::util } -inline std::ostream & operator << (std::ostream & s, std::type_info const & type) +namespace std { - s << psemek::util::type_name(type); - return s; -} -inline std::ostream & operator << (std::ostream & s, std::type_index const & type) -{ - s << psemek::util::type_name(type); - return s; + inline ostream & operator << (ostream & s, type_info const & type) + { + s << psemek::util::type_name(type); + return s; + } + + inline ostream & operator << (ostream & s, type_index const & type) + { + s << psemek::util::type_name(type); + return s; + } + } #include