// This file is part of the 64k demo project. // Provides error checking macros for recoverable errors with early return. // Unlike FATAL_XXX, these allow the caller to handle errors gracefully. #pragma once #include // Build Mode Behavior: // - Debug/STRIP_ALL: Full error messages with file:line info // - Messages stripped in STRIP_ALL but control flow preserved // // Unlike FATAL_XXX which abort(), these macros return to caller. #if !defined(STRIP_ALL) // ============================================================================== // Debug / Development: Full error messages enabled // ============================================================================== // Simple error check with immediate return // Usage: CHECK_RETURN_IF(ptr == nullptr, nullptr, "Asset not found: %s", name); // // If condition is TRUE (error detected): // - Prints "Error: [file.cc:line]" to stderr // - Returns the specified value // // Example output: // Error: Asset not found: NOISE_TEX [asset_manager.cc:87] #define CHECK_RETURN_IF(cond, retval, ...) \ do { \ if (cond) { \ fprintf(stderr, "Error: " __VA_ARGS__); \ fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ return retval; \ } \ } while (0) // Block-based error check with cleanup // Usage: // CHECK_RETURN_BEGIN(ptr == nullptr, nullptr) // if (out_size) *out_size = 0; // delete[] buffer; // ERROR_MSG("Failed to allocate: %d bytes", size); // CHECK_RETURN_END // // Allows cleanup code before return. #define CHECK_RETURN_BEGIN(cond, retval) if (cond) { #define ERROR_MSG(...) \ do { \ fprintf(stderr, "Error: " __VA_ARGS__); \ fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ } while (0) #define CHECK_RETURN_END } // Warning message (non-fatal, execution continues) // Usage: WARN_IF(count == 0, "No items found"); // // Prints warning but does NOT return. #define WARN_IF(cond, ...) \ do { \ if (cond) { \ fprintf(stderr, "Warning: " __VA_ARGS__); \ fprintf(stderr, "\n"); \ } \ } while (0) #else // ============================================================================== // STRIP_ALL: Messages stripped, control flow preserved // ============================================================================== // Simple check - silent return on error #define CHECK_RETURN_IF(cond, retval, ...) \ do { \ if (cond) \ return retval; \ } while (0) // Block-based check - cleanup code preserved, messages stripped #define CHECK_RETURN_BEGIN(cond, retval) if (cond) { #define ERROR_MSG(...) ((void)0) #define CHECK_RETURN_END } // Warning - completely stripped #define WARN_IF(cond, ...) ((void)0) #endif /* !defined(STRIP_ALL) */ // ============================================================================== // Usage Guidelines // ============================================================================== // // When to use CHECK_RETURN_IF vs CHECK_RETURN_BEGIN/END: // // CHECK_RETURN_IF: // - Simple error checks with no cleanup needed (90% of cases) // - Input validation: CHECK_RETURN_IF(argc < 2, 1, "Too few arguments") // - Null checks: CHECK_RETURN_IF(ptr == nullptr, nullptr, "Not found: %s", // name) // - Range checks: CHECK_RETURN_IF(x < 0 || x > max, -1, "Out of range") // // CHECK_RETURN_BEGIN/END: // - Error checks that need cleanup before return (10% of cases) // - Free memory: delete[] buffer // - Set output params: if (out_size) *out_size = 0 // - Release resources: close(fd), fclose(file) // // WARN_IF: // - Non-critical issues that don't prevent execution // - Deprecated features, missing optional config // - Example: WARN_IF(config_missing, "Using default config") // // ============================================================================== // Examples // ============================================================================== // // Simple validation: // CHECK_RETURN_IF(name == nullptr, false, "Invalid name parameter"); // // With cleanup: // CHECK_RETURN_BEGIN(!buffer, nullptr) // if (out_size) *out_size = 0; // ERROR_MSG("Failed to allocate %d bytes", size); // CHECK_RETURN_END // // Warning: // WARN_IF(file_not_found, "Config file missing, using defaults"); // // ============================================================================== // Comparison with FATAL_XXX // ============================================================================== // // Use FATAL_XXX for: // - Programming errors (assertion failures, invariant violations) // - Corrupted state that cannot be recovered // - Internal consistency checks // - Example: FATAL_CHECK(idx >= size, "Index out of bounds: %d >= %d", idx, // size) // // Use CHECK_RETURN for: // - Recoverable errors (invalid input, missing files) // - Runtime configuration issues // - API parameter validation // - Example: CHECK_RETURN_IF(file == nullptr, false, "File not found: %s", // path) // // Key difference: // - FATAL_XXX: abort() - program terminates // - CHECK_RETURN: return error_value - caller can handle error //