216 lines
7.8 KiB
C++
216 lines
7.8 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>
|
|
|
|
#include "../internal/common.hpp"
|
|
|
|
#if MIJIN_THROWING_ASSERTS
|
|
#include <sstream>
|
|
#include <stdexcept> // 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 <unistd.h>
|
|
#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<bool>(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<bool>(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)
|