0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-10-30 10:28:55 +01:00
construct/include/ircd/exception.h
2018-02-05 21:24:34 -08:00

184 lines
8.2 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// 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
namespace ircd
{
// Root exception
struct exception;
// Prefer ircd::terminate() to std::terminate() if possible.
[[noreturn]] void terminate(const std::exception &) noexcept;
[[noreturn]] void terminate(std::exception_ptr) noexcept;
[[noreturn]] void terminate() noexcept;
// Terminates in debug mode; throws in release mode; always logs critical.
[[noreturn]] void assertion(const std::exception &) noexcept(RB_DEBUG_LEVEL);
[[noreturn]] void assertion(std::exception_ptr) noexcept(RB_DEBUG_LEVEL);
[[noreturn]] void assertion() noexcept(RB_DEBUG_LEVEL);
// util
std::exception_ptr make_system_error(const int &code = errno);
[[noreturn]] void throw_system_error(const int &code = errno);
// Can be used to clobber the std::terminate_handler
void aborting() noexcept;
}
/// The root exception type.
///
/// All exceptions in the project inherit from this type.
/// To catch any exception from a project developer's code:
/// catch(const ircd::exception &) {}
///
/// Remember: not all exceptions are from project developer's code,
/// such as std::out_of_range. In most contexts if you have to deal with this
/// someone else was lazy, which is bad. To be sure and catch any exception:
/// catch(const std::exception &) {}
///
/// Remember: not all exceptions have to inherit from std::exception, but
/// those are rogue exceptions. We do not allow this. To be sure nothing
/// can possibly get through, add to the bottom:
/// catch(...) {}
///
/// Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert'
/// for exceptions, and the rogue can be found-out in testing.
///
struct ircd::exception
:std::exception
{
protected:
IRCD_OVERLOAD(generate_skip)
char buf[BUFSIZE];
ssize_t generate(const char *const &name, const char *const &fmt, const va_rtti &ap) noexcept;
ssize_t generate(const char *const &fmt, const va_rtti &ap) noexcept;
public:
const char *what() const noexcept final override
{
return buf;
}
exception(const generate_skip_t = {}) noexcept
{
buf[0] = '\0';
}
};
/// 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 name \
:parent \
{ \
template<class... args> \
name(const char *const &fmt = " ", args&&... ap) noexcept \
:parent{generate_skip} \
{ \
generate(#name, fmt, ircd::va_rtti{std::forward<args>(ap)...}); \
} \
\
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 name \
:parent \
{ \
template<class... args> \
name(const char *const &fmt = " ", args&&... ap) noexcept \
:parent{generate_skip} \
{ \
generate(fmt, ircd::va_rtti{std::forward<args>(ap)...}); \
} \
\
name(generate_skip_t = {}) noexcept \
:parent{generate_skip} \
{ \
} \
};
/// Creates an assertion-type exception.
///
/// Throwable exception which will terminate on construction in debug mode
/// but throw normally in release mode. Ideally this should never be thrown
/// in release mode because the termination in debug means a test can never
/// pass and the triggering callsite should be eliminated. Nevertheless it
/// throws normally in release mode.
#define IRCD_ASSERTION(parent, name) \
struct name \
:parent \
{ \
template<class... args> \
name(const char *const &fmt = " ", args&&... ap) noexcept(RB_DEBUG_LEVEL) \
:parent{generate_skip} \
{ \
generate(#name, fmt, ircd::va_rtti{std::forward<args>(ap)...}); \
ircd::assertion(*this); \
} \
\
name(generate_skip_t) noexcept(RB_DEBUG_LEVEL) \
: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")
// Assertion errors; see IRCD_ASSERTION docs.
IRCD_ASSERTION(exception, assertive)
IRCD_ASSERTION(assertive, not_implemented)
}