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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
// This file is part of the 64k demo project.
// Provides fatal error checking macros that can be stripped for final builds.
// These macros compile to nothing when FINAL_STRIP is enabled.
#pragma once
#include <cstdio>
#include <cstdlib>
// Build Mode Hierarchy:
// - Debug: Full error checking with messages and file:line info
// - STRIP_ALL: Full error checking (same as Debug)
// - FINAL_STRIP: NO error checking (all macros compile to nothing)
//
// FINAL_STRIP implies STRIP_ALL (stricter superset)
#if defined(FINAL_STRIP)
// ==============================================================================
// FINAL_STRIP: All error checking compiled out (zero runtime cost)
// ==============================================================================
// Conditional fatal error check - compiles to nothing
#define FATAL_CHECK(cond, ...) ((void)0)
// Unconditional fatal error - compiles to nothing
#define FATAL_ERROR(...) ((void)0)
// Unreachable code marker - compiles to nothing
#define FATAL_UNREACHABLE() ((void)0)
// Assertion-style check (for documenting invariants) - compiles to nothing
#define FATAL_ASSERT(cond) ((void)0)
// Block-based error checking - code inside is stripped entirely
#define FATAL_CODE_BEGIN if (0) {
#define FATAL_CODE_END }
#else
// ==============================================================================
// Debug / STRIP_ALL: Full error checking enabled
// ==============================================================================
// Conditional fatal error check with formatted message
// Usage: FATAL_CHECK(x >= max, "x out of bounds: %d >= %d\n", x, max);
//
// If condition is TRUE (error detected):
// - Prints "FATAL: <message> [file.cc:line]" to stderr
// - Calls abort()
//
// Example output:
// FATAL: write_pos out of bounds: write=100 >= capacity=50
// [ring_buffer.cc:57]
#define FATAL_CHECK(cond, ...) \
do { \
if (cond) { \
fprintf(stderr, "FATAL: " __VA_ARGS__); \
fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \
abort(); \
} \
} while (0)
// Unconditional fatal error (for impossible code paths)
// Usage: FATAL_ERROR("Should never reach here!");
//
// Always prints message and aborts.
//
// Example output:
// FATAL: Should never reach here! [main.cc:123]
#define FATAL_ERROR(...) \
do { \
fprintf(stderr, "FATAL: " __VA_ARGS__); \
fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \
abort(); \
} while (0)
// Unreachable code marker (for switch default cases, impossible branches)
// Usage:
// switch (type) {
// case A: return handle_a();
// case B: return handle_b();
// default: FATAL_UNREACHABLE();
// }
#define FATAL_UNREACHABLE() FATAL_ERROR("Unreachable code executed")
// Assertion-style check (for documenting invariants)
// Usage: FATAL_ASSERT(buffer != nullptr);
//
// If condition is FALSE (assertion failed):
// - Prints "FATAL: Assertion failed: <condition> [file.cc:line]"
// - Calls abort()
//
// Example output:
// FATAL: Assertion failed: buffer != nullptr [audio.cc:42]
#define FATAL_ASSERT(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "FATAL: Assertion failed: %s [%s:%d]\n", #cond, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
// Block-based error checking (for complex validation logic)
// Usage:
// FATAL_CODE_BEGIN
// static int callback_depth = 0;
// ++callback_depth;
// if (callback_depth > 1) {
// FATAL_ERROR("Callback re-entered! depth=%d", callback_depth);
// }
// FATAL_CODE_END
//
// The entire block is stripped when FINAL_STRIP is enabled.
#define FATAL_CODE_BEGIN
#define FATAL_CODE_END
#endif /* defined(FINAL_STRIP) */
// ==============================================================================
// Usage Guidelines
// ==============================================================================
//
// When to use each macro:
//
// FATAL_CHECK(cond, msg, ...):
// - Most common use case (90% of error checks)
// - Bounds checking: FATAL_CHECK(idx >= size, "Index %d >= %d\n", idx, size)
// - Null checking: FATAL_CHECK(ptr == nullptr, "Null pointer: %s\n", name)
// - Range validation: FATAL_CHECK(x < min || x > max, "Out of range\n")
//
// FATAL_ERROR(msg, ...):
// - Unconditional errors (should-never-happen cases)
// - Example: FATAL_ERROR("Invalid state: %d\n", state)
//
// FATAL_UNREACHABLE():
// - Switch default cases that should never be reached
// - Impossible code paths after exhaustive checks
// - Example: switch (enum) { ... default: FATAL_UNREACHABLE(); }
//
// FATAL_ASSERT(cond):
// - Documenting invariants (not error handling)
// - Pre/post conditions
// - Example: FATAL_ASSERT(count > 0 && count < MAX_COUNT)
//
// FATAL_CODE_BEGIN/END:
// - Complex validation logic that cannot be expressed as single condition
// - Temporary state tracking for validation
// - Example: Callback re-entry detection with static counter
//
// ==============================================================================
// Size Impact
// ==============================================================================
//
// Debug / STRIP_ALL builds:
// - Each FATAL_CHECK: ~50-80 bytes (depends on message length)
// - Includes fprintf() call, error message string, file:line info
//
// FINAL_STRIP builds:
// - All macros: 0 bytes (compiled out entirely)
// - Estimated savings: ~500-600 bytes for typical project
//
// ==============================================================================
// Testing Strategy
// ==============================================================================
//
// Always test both modes before committing:
//
// 1. Normal build (error checking enabled):
// cmake -S . -B build
// cmake --build build && cd build && ctest
//
// 2. FINAL_STRIP build (error checking stripped):
// cmake -S . -B build_final -DDEMO_FINAL_STRIP=ON
// cmake --build build_final
//
// Or use convenience commands:
// make final (CMake target)
// ./scripts/build_final.sh (Script)
//
|