summaryrefslogtreecommitdiff
path: root/src/util/check_return.h
blob: 89ed4bcd08e7b3cd752870da90645bf3eae28b6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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 <cstdio>

// 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: <message> [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
//