#pragma once #if !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED) #define MIJIN_DEBUG_ASSERT_HPP_INCLUDED 1 #include #include #include #include "../internal/common.hpp" #if MIJIN_THROWING_ASSERTS #include #include // I'd prefer mijin Exceptions here, but that would result in a circual include :/ #endif #ifdef _WIN32 #pragma comment(lib, "kernel32") extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define MIJIN_TRAP() DebugBreak() #else // _WIN32 #include #define MIJIN_TRAP() \ { \ const pid_t tid = gettid(); \ const pid_t pid = getpid(); \ __asm__ __volatile__ \ ( \ "syscall" \ : \ : "D"(pid), "S"(tid), "d"(5), "a"(0xea) \ : "rcx", "r11", "memory" \ ); \ } #endif // !_WIN32 namespace mijin { // // public defines // #if MIJIN_DEBUG #define MIJIN_RAISE_ERROR(msg, source_loc) \ switch (mijin::handleError(msg, source_loc)) \ { \ case mijin::ErrorHandling::CONTINUE: \ break; \ case mijin::ErrorHandling::TRAP: \ MIJIN_TRAP(); \ break; \ default: /* ABORT */ \ std::abort(); \ } #define MIJIN_ERROR(msg) \ MIJIN_RAISE_ERROR(msg, std::source_location::current()) #define MIJIN_FATAL(msg) \ MIJIN_ERROR(msg) \ std::abort() // TODO: make ignoreAll work (static variables cannot be used in constexpr functions) #define MIJIN_ASSERT(condition, msg) \ do \ { \ if (!static_cast(condition)) \ { \ /* static bool ignoreAll = false; */ \ if (true) /*!ignoreAll */ \ { \ const mijin::AssertionResult assertion_result__ = mijin::handleAssert( \ #condition, msg, std::source_location::current()); \ switch (assertion_result__) \ { \ case mijin::AssertionResult::ABORT: \ std::abort(); \ break; \ case mijin::AssertionResult::IGNORE: \ /*break*/; \ case mijin::AssertionResult::IGNORE_ALL: \ /* ignoreAll = true; */ \ break; \ default: /* ERROR */ \ MIJIN_ERROR("Debug assertion failed: " #condition "\nMessage: " msg); \ break; \ } \ } \ } \ } while(false) #define MIJIN_ASSERT_FATAL(condition, msg) \ if (!static_cast(condition)) \ { \ MIJIN_FATAL("Debug assertion failed: " #condition "\nMessage: " msg); \ } #else // MIJIN_DEBUG #define MIJIN_ERROR(...) ((void)0) #define MIJIN_FATAL(...) std::abort() #define MIJIN_ASSERT(...) ((void)0) #define MIJIN_ASSERT_FATAL(...) ((void)0) #endif // !MIJIN_DEBUG // // public constants // // // public types // #if defined(ERROR) #error "Someone included windows.h! Include mijin/util/winundef.h." #endif enum class AssertionResult { ABORT = -1, // call std::abort() ERROR = 0, // raise an error using MIJIN_ERROR() handling IGNORE = 1, // do nothing IGNORE_ALL = 2 // do nothing and nevery halt again (not implemented yet) }; enum class ErrorHandling { TRAP = 0, CONTINUE = 1, ABORT = 2 }; #if MIJIN_THROWING_ASSERTS namespace impl { inline std::string formatAssertionExceptionMessage(const char* condition, const char* message, const std::source_location& location) { std::ostringstream oss; oss << "Assertion failed!\n" << "Condition: " << condition << "\n" << "Message: " << message << "\n" << "Location: " << location.file_name() << ":" << location.line() << ":" << location.column() << "\n" << "Function: " << location.function_name(); return oss.str(); } inline std::string formatErrorExceptionMessage(const char* message, const std::source_location& location) { std::ostringstream oss; oss << "Mijin error!\n" << "Message: " << message << "\n" << "Location: " << location.file_name() << ":" << location.line() << ":" << location.column() << "\n" << "Function: " << location.function_name(); return oss.str(); } } class AssertionException : public std::runtime_error { public: AssertionException(const char* condition, const char* message, const std::source_location& location) : std::runtime_error(impl::formatAssertionExceptionMessage(condition, message, location)) { } }; class ErrorException : public std::runtime_error { public: ErrorException(const char* message, const std::source_location& location) : std::runtime_error(impl::formatErrorExceptionMessage(message, location)) { } }; #endif // // public functions // #ifdef MIJIN_USE_CUSTOM_ASSERTION_HANDLER AssertionResult handleAssert(const char* condition, const char* message, const std::source_location& location); #elif MIJIN_THROWING_ASSERTS inline AssertionResult handleAssert(const char* condition, const char* message, const std::source_location& location) { throw AssertionException(condition, message, location); } #else constexpr AssertionResult handleAssert(const char* /* condition */, const char* /* message */, const std::source_location& /* location */) { return AssertionResult::ERROR; } #endif // MIJIN_USE_CUSTOM_ASSERTION_HANDLER #ifdef MIJIN_USE_CUSTOM_ERROR_HANDLER ErrorHandling handleError(const char* message, const std::source_location& location); #else inline ErrorHandling handleError(const char* message, const std::source_location& location) MIJIN_NOEXCEPT { #if MIJIN_THROWING_ASSERTS throw ErrorException(message, location); #else std::puts(message); std::printf("Function: %s\n", location.function_name()); std::printf("Location: %s:%d:%d\n", location.file_name(), location.line(), location.column()); (void) std::fflush(stdout); return ErrorHandling::TRAP; #endif } #endif } // namespace mijin #endif // !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED)