#ifndef GUF_ASSERT_H #define GUF_ASSERT_H #include #include #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