libguf/src/guf_assert.h
2025-02-19 08:18:18 +01:00

158 lines
4.2 KiB
C

#ifndef GUF_ASSERT_H
#define GUF_ASSERT_H
#include <stdio.h>
#include <assert.h>
#include "guf_common.h"
typedef enum guf_err {
GUF_ERR_NONE = 0,
GUF_ERR_UNSPECIFIED,
GUF_ERR_ALLOC_FAIL,
GUF_ERR_NULL_PTR,
GUF_ERR_INT_OVERFLOW,
GUF_ERR_DIV_BY_ZERO,
GUF_ERR_DOMAIN,
GUF_ERR_IDX_RANGE,
GUF_ERR_INVALID_ARG,
GUF_ERR_RUNTIME,
GUF_ERR_LOGIC,
GUF_ERR_NOT_FOUND,
GUF_ERR_ALREADY_EXISTS,
GUF_ERR_ASSERT_FAIL,
GUF_ERR_TYPES_NUM
} guf_err;
typedef void(*guf_panic_handler_fn)(guf_err err, const char *msg);
extern guf_panic_handler_fn guf_global_panic_handler;
extern void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler);
extern void guf_panic_handler_default(guf_err err, const char *msg);
extern const char *guf_err_to_str(guf_err err);
#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__)
#define GUF_ERR_MSG(msg) msg " (" GUF_FILE_LINE_STR() ")"
#define GUF_ERR_MSG_EMPTY() "(" GUF_FILE_LINE_STR() ")"
extern void guf_panic(guf_err err, const char *msg);
#define GUF_PANIC(err) guf_panic(err, "(" GUF_FILE_LINE_STR() ")");
// Break on debug, cf. https://nullprogram.com/blog/2022/06/26/ (last retrieved 2025-01-07)
#if __GNUC__ || __clang__
#define GUF_DEBUG_BREAK_CODE __builtin_trap()
#elif _MSC_VER
#define GUF_DEBUG_BREAK_CODE __debugbreak()
#else
#define GUF_DEBUG_BREAK_CODE abort()
#endif
#ifndef NDEBUG
#define GUF_ASSERT(COND) do { \
if (!(COND)) { \
guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \
abort(); \
}\
} while(0);
#else
#define GUF_ASSERT(COND)
#endif
#define GUF_ASSERT_RELEASE(COND) do { \
if (!(COND)) { \
guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(release assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \
abort();\
} \
} while (0);
static inline void guf_err_set_or_panic(guf_err *err, guf_err err_val, const char *panic_msg)
{
if (!err) {
guf_panic(err_val, panic_msg);
} else {
*err = err_val;
}
}
static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
{
if (err) {
*err = err_val;
}
}
#endif
#ifdef GUF_INIT
#undef GUF_INIT
static const char *guf_err_type_str[] = {
[GUF_ERR_NONE] = "Not an error",
[GUF_ERR_UNSPECIFIED] = "Error",
[GUF_ERR_ALLOC_FAIL] = "Allocation error",
[GUF_ERR_NULL_PTR] = "Null pointer dereference",
[GUF_ERR_DOMAIN] = "Domain error",
[GUF_ERR_INT_OVERFLOW] = "Integer overflow",
[GUF_ERR_DIV_BY_ZERO] = "Division by zero",
[GUF_ERR_IDX_RANGE] = "Index out of range",
[GUF_ERR_INVALID_ARG] = "Invalid argument",
[GUF_ERR_NOT_FOUND] = "Not found error",
[GUF_ERR_ALREADY_EXISTS] = "Already exists error",
[GUF_ERR_RUNTIME] = "Runtime error",
[GUF_ERR_LOGIC] = "Logic error",
[GUF_ERR_ASSERT_FAIL] = "Assertion failed"
};
extern void guf_panic(guf_err err, const char *msg)
{
if (!guf_global_panic_handler) {
fputs("libguf panic (note: guf_global_panic_handler is NULL)\n", stderr);
if (msg) {
fputs(": ", stderr);
fputs(msg, stderr);
}
abort();
}
guf_global_panic_handler(err, msg);
abort(); // Just in case...
}
extern void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler)
{
guf_global_panic_handler = panic_handler;
}
extern const char *guf_err_to_str(guf_err err)
{
if (GUF_STATIC_BUF_SIZE(guf_err_type_str) != GUF_ERR_TYPES_NUM) {
puts("Note: size of guf_err_type_str != GUF_ERR_TYPES_NUM");
}
if (err < 0 || err >= GUF_ERR_TYPES_NUM) {
return "Invalid error code";
} else {
if (err > GUF_STATIC_BUF_SIZE(guf_err_type_str)) {
return "Invalid error string";
}
return guf_err_type_str[err];
}
}
extern void guf_panic_handler_default(guf_err err, const char *msg)
{
fputs("libguf panic: ", stderr);
fputs(guf_err_to_str(err), stderr);
fputc(' ', stderr);
if (msg && err != GUF_ERR_NONE) {
fputs(msg, stderr);
}
fputc('\n', stderr);
#ifndef NDEBUG
GUF_DEBUG_BREAK_CODE;
#endif
abort();
}
guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default;
#endif