// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_EXCEPTION_H /// Forward declarations for boost::system because it is not included here. namespace boost::system { struct error_code; struct error_category; struct system_error; namespace errc {} } namespace ircd { struct exception; // Root exception // util bool system_category(const std::error_category &) noexcept; bool system_category(const std::error_code &) noexcept; bool system_category(const boost::system::error_code &) noexcept; bool is(const std::error_code &, const std::errc &) noexcept; bool is(const boost::system::error_code &, const std::errc &) noexcept; std::error_code make_error_code(const int &code = errno) noexcept; std::error_code make_error_code(const std::error_code &) noexcept; std::error_code make_error_code(const std::system_error &) noexcept; std::error_code make_error_code(const boost::system::error_code &) noexcept; std::error_code make_error_code(const boost::system::system_error &) noexcept; std::system_error make_system_error(const int &code = errno); std::system_error make_system_error(const std::errc &); std::system_error make_system_error(const std::error_code &); std::system_error make_system_error(const boost::system::error_code &); std::system_error make_system_error(const boost::system::system_error &); [[noreturn]] void throw_system_error(); template std::exception_ptr make_system_eptr(args&&...); template [[noreturn]] void throw_system_error(args&&...); template std::exception_ptr make_exception_ptr(args&&...); string_view string(const mutable_buffer &, const std::error_code &); string_view string(const mutable_buffer &, const std::system_error &); string_view string(const mutable_buffer &, const boost::system::error_code &); string_view string(const mutable_buffer &, const boost::system::system_error &); std::string string(const std::error_code &); std::string string(const std::system_error &); std::string string(const boost::system::error_code &); std::string string(const boost::system::system_error &); } /// The root exception type. /// /// All exceptions in the project inherit from this type. We generally don't /// have catch blocks on this type. Instead we use `ircd::error` to catch /// project-specific exceptions. This gives us just a little more indirection /// to play with before inheriting from std::exception. /// /// Not all exceptions are from project developer's code, such as /// `std::out_of_range`, etc. It's not necessarily bad to just catch /// `std::exception` and we do it often enough, but be considerate. /// /// Remember: Not all exceptions have to inherit from `std::exception` either. /// We have only one example of this: ctx::terminated. To be sure nothing can /// possibly get through you can `catch(...)` but with extreme care that you /// are not discarding a termination which will hang the `ircd::ctx` you're on. /// /// !!! /// NOTE: CONTEXT SWITCHES CANNOT OCCUR INSIDE CATCH BLOCKS UNLESS YOU USE THE /// MITIGATION TOOLS PROVIDED IN `ircd::ctx` WHICH RESULT IN THE LOSS OF THE /// ABILITY TO RETHROW THE EXCEPTION (i.e `throw` statement). BEST PRACTICE /// IS TO RETURN CONTROL FROM THE CATCH BLOCK BEFORE CONTEXT SWITCHING. /// !!! struct ircd::exception :virtual std::exception { static constexpr const size_t BUFSIZE { 512UL }; IRCD_OVERLOAD(generate_skip) IRCD_OVERLOAD(hide_name) protected: char buf[BUFSIZE]; ssize_t generate(const char *const &name, const string_view &fmt, const va_rtti &ap) noexcept; ssize_t generate(const string_view &fmt, const va_rtti &ap) noexcept; public: const char *what() const noexcept override; exception(generate_skip_t = {}) noexcept; exception(exception &&) = delete; exception(const exception &) = delete; exception &operator=(exception &&) = delete; exception &operator=(const exception &) = delete; ~exception() noexcept override; }; /// Exception generator convenience macro /// /// If you want to create your own exception type, you have found the right /// place! This macro allows creating an exception in the the hierarchy. /// /// To create an exception, invoke this macro in your header. Examples: /// /// IRCD_EXCEPTION(ircd::exception, my_exception) /// IRCD_EXCEPTION(my_exception, my_specific_exception) /// /// Then your catch sequence can look like the following: /// /// catch(const my_specific_exception &e) /// { /// log("something specifically bad happened: %s", e.what()); /// } /// catch(const my_exception &e) /// { /// log("something generically bad happened: %s", e.what()); /// } /// catch(const ircd::exception &e) /// { /// log("unrelated bad happened: %s", e.what()); /// } /// catch(const std::exception &e) /// { /// log("unhandled bad happened: %s", e.what()); /// } /// /// Remember: the order of the catch blocks is important. /// #define IRCD_EXCEPTION(parent, name) \ struct [[gnu::visibility("protected")]] name \ :parent \ { \ template \ [[gnu::noinline]] \ name(hide_name_t, const string_view &fmt, args&&... ap) noexcept \ :parent{generate_skip} \ { \ generate(fmt, ircd::va_rtti{std::forward(ap)...}); \ } \ \ template \ [[gnu::noinline]] \ name(hide_name_t, const string_view &fmt = " ") noexcept \ :parent{generate_skip} \ { \ generate(fmt, ircd::va_rtti{}); \ } \ \ template \ [[gnu::noinline]] \ name(const string_view &fmt, args&&... ap) noexcept \ :parent{generate_skip} \ { \ generate(#name, fmt, ircd::va_rtti{std::forward(ap)...}); \ } \ \ template \ [[gnu::noinline]] \ name(const string_view &fmt = " ") noexcept \ :parent{generate_skip} \ { \ generate(#name, fmt, ircd::va_rtti{}); \ } \ \ [[using gnu: flatten, always_inline]] \ name(generate_skip_t) noexcept \ :parent{generate_skip} \ { \ } \ }; /// Hides the name of the exception when generating a string #define IRCD_EXCEPTION_HIDENAME(parent, name) \ struct [[gnu::visibility("protected")]] name \ :parent \ { \ template \ [[gnu::noinline]] \ name(const string_view &fmt, args&&... ap) noexcept \ :parent{generate_skip} \ { \ generate(fmt, ircd::va_rtti{std::forward(ap)...}); \ } \ \ template \ [[gnu::noinline]] \ name(const string_view &fmt = " ") noexcept \ :parent{generate_skip} \ { \ generate(fmt, ircd::va_rtti{}); \ } \ \ [[using gnu: flatten, always_inline]] \ name(generate_skip_t = {}) noexcept \ :parent{generate_skip} \ { \ } \ }; namespace ircd { /// Root error exception type. Inherit from this. /// List your own exception somewhere else (unless you're overhauling libircd). /// example, in your namespace: /// /// IRCD_EXCEPTION(ircd::error, error) /// IRCD_EXCEPTION(exception, error) // throw ircd::error("something bad") IRCD_EXCEPTION(error, user_error) // throw ircd::user_error("something silly") } template inline std::exception_ptr ircd::make_exception_ptr(args&&... a) try { throw E{std::forward(a)...}; } catch(const E &) { return std::current_exception(); }; template inline void ircd::throw_system_error(args&&... a) { throw make_system_error(std::forward(a)...); } template inline std::exception_ptr ircd::make_system_eptr(args&&... a) { return std::make_exception_ptr(make_system_error(std::forward(a)...)); } [[gnu::always_inline]] inline ircd::exception::exception(generate_skip_t) noexcept { buf[0] = '\0'; }