#pragma once #if !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED) #define MIJIN_DEBUG_ASSERT_HPP_INCLUDED 1 #include #include #include #include "../internal/common.hpp" #ifdef _WIN32 #pragma comment(lib, "kernel32") extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define MIJIN_TRAP() DebugBreak() #define MIJIN_FUNC() __FUNCSIG__ #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" \ ); \ } #define MIJIN_FUNC() "" // TODO: __PRETTY_FUNCTION__ is not working for some reason -.- #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(...) #define MIJIN_FATAL(...) std::abort() #define MIJIN_ASSERT(...) #define MIJIN_ASSERT_FATAL(...) #endif // !MIJIN_DEBUG // // public constants // // // public types // 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 }; // // public functions // #ifdef MIJIN_USE_CUSTOM_ASSERTION_HANDLER AssertionResult handleAssert(const char* condition, const char* message, const std::source_location& 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 { 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 } // namespace mijin #endif // !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED)