#pragma once #if !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED) #define MIJIN_DEBUG_ASSERT_HPP_INCLUDED 1 #include #include namespace mijin { // // public defines // #if MIJIN_DEBUG #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() raise(SIGTRAP) #define MIJIN_FUNC() "" // TODO: __PRETTY_FUNCTION__ is not working for some reason -.- #endif // !_WIN32 #define MIJIN_DO_RAISE_ERROR(msg, func, file, line) \ switch (mijin::handleError(msg, func, file, line)) \ { \ case mijin::ErrorHandling::CONTINUE: \ break; \ case mijin::ErrorHandling::TRAP: \ MIJIN_TRAP(); \ [[fallthrough]]; \ default: /* ABORT */ \ std::abort(); \ } #define MIJIN_RAISE_ERROR(msg, func, file, line) MIJIN_DO_RAISE_ERROR(msg, func, file, line) #define MIJIN_ERROR(msg) \ MIJIN_RAISE_ERROR(msg, MIJIN_FUNC(), __FILE__, __LINE__) #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) \ if (!static_cast(condition)) \ { \ /* static bool ignoreAll = false; */ \ if (true) /*!ignoreAll */ \ { \ mijin::AssertionResult assertion_result__ = mijin::handleAssert(#condition,\ msg, MIJIN_FUNC(), __FILE__, __LINE__); \ 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; \ } \ } \ } #define MIJIN_ASSERT_FATAL(condition, msg) \ if (!static_cast(condition)) \ { \ MIJIN_ERROR("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 char* function, const char* file, int line) noexcept; #else constexpr AssertionResult handleAssert(const char* /* condition */, const char* /* message */, const char* /* function */, const char* /* file */, int /* line */) { return AssertionResult::ERROR; } #endif // MIJIN_USE_CUSTOM_ASSERTION_HANDLER #ifdef MIJIN_USE_CUSTOM_ERROR_HANDLER ErrorHandling handleError(const char* message, const char* function, const char* file, int line) noexcept; #else inline ErrorHandling handleError(const char* message, const char* function, const char* file, int line) noexcept { std::puts(message); std::printf("Function: %s\n", function); std::printf("File: %s\n", file); std::printf("Line: %d\n", line); (void) std::fflush(stdout); return ErrorHandling::TRAP; } #endif } // namespace mijin #endif // !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED)