enum ErrorSeverity: s32 { SEVERITY_WARNING = 0, SEVERITY_NON_FATAL = 1, SEVERITY_FATAL = 2 }; // typedef struct string Native_Error; // Note: Native_Error should down-cast to a string. struct Native_Error { s64 count; u8* data; ErrorSeverity severity = SEVERITY_WARNING; }; #define Null_Pointer_Check(arg) \ if (arg == nullptr) { \ return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is a null pointer.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \ } #define Array_Check(arg) \ if (!is_valid(arg)) { \ return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid array.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \ } #define String_Check(arg) \ if (!Is_Valid(arg)) { return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid string.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); } #define Error_Check(error) \ if (error != nullptr) { \ return error; \ } // An error from which the program cannot continue (e.g. a segmentation fault) #define New_Fatal_Error(message) \ New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message) #define New_Error(message) \ New_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message) #define New_Warning(message) \ New_Warning_Internal("%s:%d\n[%s] Warning: %s.", __FILE__, __LINE__, __FUNCTION__, message) Native_Error* New_Fatal_Error_Internal(char* raw_message, ...); Native_Error* New_Error_Internal(char* raw_message, ...); Native_Error* New_Warning_Internal(char* raw_message, ...); Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity); PROTOTYPING_API C_API Native_Error* Cleanup_Error(Native_Error* error); PROTOTYPING_API C_API Native_Error* Native_Error_Test(); #include "General_Purpose_Allocator.h" #include // vsnprintf, printf #include // va_list... #define BREAK_ON_WARNINGS 0 #define BREAK_ON_ERRORS 0 #define BREAK_ON_FATAL_ERROR BUILD_DEBUG #define ALWAYS_PRINT_ERROR_MESSAGES BUILD_DEBUG Native_Error* Create_New_Native_Error_Internal(char* format, va_list args) { constexpr s64 ERROR_BUFFER_COUNT = 512; // push_allocator(GPAllocator()); auto error = New(false); error->data = (u8*)GPAllocator_New(ERROR_BUFFER_COUNT); // You MUST copy the va_list before using it more than once va_list args_copy; va_copy(args_copy, args); error->count = (s64)vsnprintf((char*)error->data, (size_t)ERROR_BUFFER_COUNT, format, args_copy); va_end(args_copy); return error; } Native_Error* New_Fatal_Error_Internal(char* format, ...) { va_list args; va_start(args, format); auto error = Create_New_Native_Error_Internal(format, args); va_end(args); error->severity = SEVERITY_FATAL; #if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES printf("[FATAL ERROR] %.*s\n", (s32)error->count, (char*)error->data); #endif #if BREAK_ON_FATAL_ERROR debug_break(); #endif return error; } Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity) { // push_allocator(GPAllocator()); auto error_message = format_string("%s\n > %s", new_error->data, old_error->data).data; Cleanup_Error(new_error); Cleanup_Error(old_error); Native_Error* error_merged = New(false); error_merged->data = (u8*)error_message; error_merged->count = strlen((char*)error_merged->data); error_merged->severity = severity; return error_merged; } Native_Error* Native_Error_Test() { // This is quite verbose, but w/e auto old_error = New_Error("Original error..."); auto new_message = format_string("Failed to start stream. Error Code: %d", -1).data; auto new_error = New_Error(new_message); GPAllocator_Delete(new_message); return Native_Error_Callstack(new_error, old_error, SEVERITY_NON_FATAL); } Native_Error* New_Error_Internal(char* format, ...) { va_list args; va_start(args, format); auto error = Create_New_Native_Error_Internal(format, args); va_end(args); error->severity = SEVERITY_NON_FATAL; #if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES printf("[ERROR (NON-FATAL)] %.*s\n", (s32)error->count, (char*)error->data); #endif #if BREAK_ON_ERRORS debug_break(); #endif return error; } Native_Error* New_Warning_Internal(char* format, ...) { va_list args; va_start(args, format); auto error = Create_New_Native_Error_Internal(format, args); va_end(args); error->severity = SEVERITY_WARNING; #if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES printf("[WARNING] %.*s\n", (s32)error->count, (char*)error->data); #endif #if BREAK_ON_WARNINGS debug_break(); #endif return error; } Native_Error* Cleanup_Error(Native_Error* error) { if (error == nullptr) return nullptr; GPAllocator_Delete(error->data); GPAllocator_Delete(error); return nullptr; }