mijin2/source/mijin/debug/assert.hpp

149 lines
5.2 KiB
C++

#pragma once
#if !defined(MIJIN_DEBUG_ASSERT_HPP_INCLUDED)
#define MIJIN_DEBUG_ASSERT_HPP_INCLUDED 1
#include <cstdio>
#include <cstdlib>
#include <source_location>
#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 <signal.h>
#define MIJIN_TRAP() raise(SIGTRAP)
#define MIJIN_FUNC() "" // TODO: __PRETTY_FUNCTION__ is not working for some reason -.-
#endif // !_WIN32
#endif // MIJIN_DEBUG
namespace mijin
{
//
// public defines
//
#if MIJIN_DEBUG
#define MIJIN_DO_RAISE_ERROR(msg, source_loc) \
switch (mijin::handleError(msg, source_loc)) \
{ \
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_DO_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) \
if (!static_cast<bool>(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<bool>(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 std::source_location& location) 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)