#ifndef ERROR_HPP #define ERROR_HPP #include #include #include #include "platform/platform.hpp" #include "utils/microfmt.hpp" #if IS_MSVC #define CPPTRACE_PFUNC __FUNCSIG__ #else #define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__ #endif namespace cpptrace { namespace detail { class internal_error : public std::exception { std::string msg; public: internal_error(std::string message) : msg("Cpptrace internal error: " + std::move(message)) {} template internal_error(const char* format, Args&&... args) : internal_error(microfmt::format(format, args...)) {} const char* what() const noexcept override { return msg.c_str(); } }; // Lightweight std::source_location. struct source_location { const char* const file; const int line; constexpr source_location( const char* _file, int _line ) : file(_file), line(_line) {} }; #define CPPTRACE_CURRENT_LOCATION ::cpptrace::detail::source_location(__FILE__, __LINE__) enum class assert_type { assert, verify, panic, }; constexpr const char* assert_actions[] = {"assertion", "verification", "panic"}; constexpr const char* assert_names[] = {"ASSERT", "VERIFY", "PANIC"}; [[noreturn]] inline void assert_fail( assert_type type, const char* expression, const char* signature, source_location location, const char* message ) { const char* action = assert_actions[static_cast::type>(type)]; const char* name = assert_names[static_cast::type>(type)]; if(message == nullptr) { throw internal_error( "Cpptrace {} failed at {}:{}: {}\n" " {}({});\n", action, location.file, location.line, signature, name, expression ); } else { throw internal_error( "Cpptrace {} failed at {}:{}: {}: {}\n" " {}({});\n", action, location.file, location.line, signature, message, name, expression ); } } [[noreturn]] inline void panic( const char* signature, source_location location, const std::string& message = "" ) { if(message == "") { throw internal_error( "Cpptrace panic {}:{}: {}\n", location.file, location.line, signature ); } else { throw internal_error( "Cpptrace panic {}:{}: {}: {}\n", location.file, location.line, signature, message.c_str() ); } } template void nullfn() { // this method doesn't do anything and is never called. } #define PHONY_USE(...) (nullfn()) // Work around a compiler warning template bool as_bool(T&& value) { return static_cast(std::forward(value)); } // Work around a compiler warning template std::string as_string(T&& value) { return std::string(std::forward(value)); } inline std::string as_string() { return ""; } // Check condition in both debug and release. std::runtime_error on failure. #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION, ::cpptrace::detail::as_string(__VA_ARGS__))) template void assert_impl( T condition, const char* message, assert_type type, const char* args, const char* signature, source_location location ) { if(!as_bool(condition)) { assert_fail(type, args, signature, location, message); } } template void assert_impl( T condition, assert_type type, const char* args, const char* signature, source_location location ) { assert_impl( condition, nullptr, type, args, signature, location ); } // Check condition in both debug and release. std::runtime_error on failure. #define VERIFY(...) ( \ assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::verify, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \ ) #ifndef NDEBUG // Check condition in both debug. std::runtime_error on failure. #define ASSERT(...) ( \ assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::assert, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \ ) #else // Check condition in both debug. std::runtime_error on failure. #define ASSERT(...) PHONY_USE(__VA_ARGS__) #endif } } #endif