red64-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -58
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +2 -2
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/services/AgentInvoker.js +4 -4
- package/dist/services/AgentInvoker.js.map +1 -1
- package/dist/services/ClaudeHealthCheck.d.ts +5 -0
- package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
- package/dist/services/ClaudeHealthCheck.js +43 -5
- package/dist/services/ClaudeHealthCheck.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/framework/stacks/c/code-quality.md +326 -0
- package/framework/stacks/c/coding-style.md +347 -0
- package/framework/stacks/c/conventions.md +513 -0
- package/framework/stacks/c/error-handling.md +350 -0
- package/framework/stacks/c/feedback.md +158 -0
- package/framework/stacks/c/memory-safety.md +408 -0
- package/framework/stacks/c/tech.md +122 -0
- package/framework/stacks/c/testing.md +472 -0
- package/framework/stacks/cpp/code-quality.md +282 -0
- package/framework/stacks/cpp/coding-style.md +363 -0
- package/framework/stacks/cpp/conventions.md +420 -0
- package/framework/stacks/cpp/error-handling.md +264 -0
- package/framework/stacks/cpp/feedback.md +104 -0
- package/framework/stacks/cpp/memory-safety.md +351 -0
- package/framework/stacks/cpp/tech.md +160 -0
- package/framework/stacks/cpp/testing.md +323 -0
- package/framework/stacks/java/code-quality.md +357 -0
- package/framework/stacks/java/coding-style.md +400 -0
- package/framework/stacks/java/conventions.md +437 -0
- package/framework/stacks/java/error-handling.md +408 -0
- package/framework/stacks/java/feedback.md +180 -0
- package/framework/stacks/java/tech.md +126 -0
- package/framework/stacks/java/testing.md +485 -0
- package/framework/stacks/javascript/async-patterns.md +216 -0
- package/framework/stacks/javascript/code-quality.md +182 -0
- package/framework/stacks/javascript/coding-style.md +293 -0
- package/framework/stacks/javascript/conventions.md +268 -0
- package/framework/stacks/javascript/error-handling.md +216 -0
- package/framework/stacks/javascript/feedback.md +80 -0
- package/framework/stacks/javascript/tech.md +114 -0
- package/framework/stacks/javascript/testing.md +209 -0
- package/framework/stacks/loco/code-quality.md +156 -0
- package/framework/stacks/loco/coding-style.md +247 -0
- package/framework/stacks/loco/error-handling.md +225 -0
- package/framework/stacks/loco/feedback.md +35 -0
- package/framework/stacks/loco/loco.md +342 -0
- package/framework/stacks/loco/structure.md +193 -0
- package/framework/stacks/loco/tech.md +129 -0
- package/framework/stacks/loco/testing.md +211 -0
- package/framework/stacks/rust/code-quality.md +370 -0
- package/framework/stacks/rust/coding-style.md +475 -0
- package/framework/stacks/rust/conventions.md +430 -0
- package/framework/stacks/rust/error-handling.md +399 -0
- package/framework/stacks/rust/feedback.md +152 -0
- package/framework/stacks/rust/memory-safety.md +398 -0
- package/framework/stacks/rust/tech.md +121 -0
- package/framework/stacks/rust/testing.md +528 -0
- package/package.json +14 -2
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Development Conventions
|
|
2
|
+
|
|
3
|
+
General development practices, workflow, and operational standards for C++ projects.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Predictable process**: Consistent workflows reduce friction and errors
|
|
10
|
+
- **Automated enforcement**: clang-tidy and CI catch what humans miss
|
|
11
|
+
- **Build reproducibility**: Lock dependencies, pin toolchains, use presets
|
|
12
|
+
- **Documentation as code**: Keep docs next to the code they describe
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Git Workflow
|
|
17
|
+
|
|
18
|
+
### Branch Strategy
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
main # Production-ready, always builds
|
|
22
|
+
|-- feat/... # Feature branches (short-lived)
|
|
23
|
+
|-- fix/... # Bug fix branches
|
|
24
|
+
|-- chore/... # Maintenance, dependency updates
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Branch Naming
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
feat/add-user-authentication
|
|
31
|
+
fix/buffer-overflow-in-parser
|
|
32
|
+
chore/upgrade-fmt-to-11
|
|
33
|
+
refactor/extract-http-client
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Pattern**: `{type}/{short-description}` with lowercase and hyphens.
|
|
37
|
+
|
|
38
|
+
### Workflow
|
|
39
|
+
|
|
40
|
+
1. Create branch from `main`
|
|
41
|
+
2. Make small, focused commits
|
|
42
|
+
3. Open PR when ready for review
|
|
43
|
+
4. Squash merge into `main`
|
|
44
|
+
5. Delete branch after merge
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Commit Conventions
|
|
49
|
+
|
|
50
|
+
### Conventional Commits
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
feat: add JWT authentication middleware
|
|
54
|
+
fix: prevent buffer overflow in JSON parser
|
|
55
|
+
refactor: extract HTTP client into separate library
|
|
56
|
+
test: add parameterized tests for email validation
|
|
57
|
+
docs: update CMake build instructions
|
|
58
|
+
chore: upgrade vcpkg baseline to 2025.01
|
|
59
|
+
ci: add ASan/UBSan to test matrix
|
|
60
|
+
perf: use string_view to avoid copies in request parsing
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Format
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
{type}: {short description}
|
|
67
|
+
|
|
68
|
+
{optional body explaining why, not what}
|
|
69
|
+
|
|
70
|
+
{optional footer: BREAKING CHANGE, Closes #123}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Types
|
|
74
|
+
|
|
75
|
+
| Type | Description |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `feat` | New feature or capability |
|
|
78
|
+
| `fix` | Bug fix |
|
|
79
|
+
| `refactor` | Code change that neither fixes nor adds |
|
|
80
|
+
| `test` | Adding or updating tests |
|
|
81
|
+
| `docs` | Documentation only |
|
|
82
|
+
| `chore` | Maintenance, dependencies, tooling |
|
|
83
|
+
| `ci` | CI/CD configuration changes |
|
|
84
|
+
| `perf` | Performance improvement |
|
|
85
|
+
|
|
86
|
+
**Rule**: One logical change per commit. If the commit message needs "and", split it.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## CMake Project Structure
|
|
91
|
+
|
|
92
|
+
### Standard Layout
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
myproject/
|
|
96
|
+
CMakeLists.txt # Root CMakeLists
|
|
97
|
+
CMakePresets.json # Build presets
|
|
98
|
+
vcpkg.json # Dependency manifest
|
|
99
|
+
.clang-format # Formatting config
|
|
100
|
+
.clang-tidy # Static analysis config
|
|
101
|
+
cmake/
|
|
102
|
+
CompilerWarnings.cmake # Shared warning flags
|
|
103
|
+
Sanitizers.cmake # Sanitizer build options
|
|
104
|
+
include/
|
|
105
|
+
myproject/ # Public headers
|
|
106
|
+
user_service.hpp
|
|
107
|
+
http_client.hpp
|
|
108
|
+
src/
|
|
109
|
+
user_service.cpp
|
|
110
|
+
http_client.cpp
|
|
111
|
+
main.cpp
|
|
112
|
+
tests/
|
|
113
|
+
CMakeLists.txt
|
|
114
|
+
unit/
|
|
115
|
+
test_user_service.cpp
|
|
116
|
+
integration/
|
|
117
|
+
test_database.cpp
|
|
118
|
+
benchmark/
|
|
119
|
+
bench_serialization.cpp
|
|
120
|
+
docs/
|
|
121
|
+
third_party/ # Vendored dependencies (if any)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CMakePresets.json
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"version": 6,
|
|
129
|
+
"configurePresets": [
|
|
130
|
+
{
|
|
131
|
+
"name": "default",
|
|
132
|
+
"binaryDir": "${sourceDir}/build",
|
|
133
|
+
"generator": "Ninja",
|
|
134
|
+
"cacheVariables": {
|
|
135
|
+
"CMAKE_CXX_STANDARD": "23",
|
|
136
|
+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
|
137
|
+
},
|
|
138
|
+
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "debug",
|
|
142
|
+
"inherits": "default",
|
|
143
|
+
"cacheVariables": {
|
|
144
|
+
"CMAKE_BUILD_TYPE": "Debug"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "release",
|
|
149
|
+
"inherits": "default",
|
|
150
|
+
"cacheVariables": {
|
|
151
|
+
"CMAKE_BUILD_TYPE": "Release"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"name": "asan",
|
|
156
|
+
"inherits": "debug",
|
|
157
|
+
"cacheVariables": {
|
|
158
|
+
"CMAKE_CXX_FLAGS": "-fsanitize=address,undefined -fno-omit-frame-pointer"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"buildPresets": [
|
|
163
|
+
{"name": "default", "configurePreset": "default"},
|
|
164
|
+
{"name": "release", "configurePreset": "release"},
|
|
165
|
+
{"name": "asan", "configurePreset": "asan"}
|
|
166
|
+
],
|
|
167
|
+
"testPresets": [
|
|
168
|
+
{
|
|
169
|
+
"name": "default",
|
|
170
|
+
"configurePreset": "default",
|
|
171
|
+
"output": {"outputOnFailure": true}
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Dependency Management
|
|
180
|
+
|
|
181
|
+
### vcpkg Manifest Mode (Preferred)
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"name": "myproject",
|
|
186
|
+
"version-string": "1.0.0",
|
|
187
|
+
"dependencies": [
|
|
188
|
+
"fmt",
|
|
189
|
+
"spdlog",
|
|
190
|
+
"nlohmann-json",
|
|
191
|
+
"gtest",
|
|
192
|
+
"benchmark"
|
|
193
|
+
],
|
|
194
|
+
"builtin-baseline": "a1a1cbc975e450accd1f5b7e4530e1378575f291"
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Add a dependency
|
|
200
|
+
vcpkg add port abseil
|
|
201
|
+
|
|
202
|
+
# Update baseline (all dependencies)
|
|
203
|
+
vcpkg x-update-baseline
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Dependency Rules
|
|
207
|
+
|
|
208
|
+
- Use manifest mode (`vcpkg.json`), not classic mode
|
|
209
|
+
- Pin the baseline hash in `vcpkg.json`
|
|
210
|
+
- Update baseline regularly (monthly or per sprint)
|
|
211
|
+
- Prefer header-only libraries when performance permits
|
|
212
|
+
- Vendor critical dependencies only as a last resort
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Header vs Source Organization
|
|
217
|
+
|
|
218
|
+
### Public vs Private Headers
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
include/myproject/ # Public API headers (installed with library)
|
|
222
|
+
user_service.hpp # Declarations only, minimal includes
|
|
223
|
+
src/
|
|
224
|
+
user_service.cpp # Implementations
|
|
225
|
+
internal/ # Private headers (not installed)
|
|
226
|
+
database_impl.hpp
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Include Guard Style
|
|
230
|
+
|
|
231
|
+
```cpp
|
|
232
|
+
// GOOD: #pragma once (widely supported, no name collisions)
|
|
233
|
+
#pragma once
|
|
234
|
+
|
|
235
|
+
#include <string>
|
|
236
|
+
#include <vector>
|
|
237
|
+
|
|
238
|
+
namespace myproject {
|
|
239
|
+
class UserService { /* ... */ };
|
|
240
|
+
} // namespace myproject
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```cpp
|
|
244
|
+
// ACCEPTABLE: Traditional include guards (required for some compilers)
|
|
245
|
+
#ifndef MYPROJECT_USER_SERVICE_HPP
|
|
246
|
+
#define MYPROJECT_USER_SERVICE_HPP
|
|
247
|
+
|
|
248
|
+
// ...
|
|
249
|
+
|
|
250
|
+
#endif // MYPROJECT_USER_SERVICE_HPP
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Preference**: Use `#pragma once` unless the project must support exotic compilers.
|
|
254
|
+
|
|
255
|
+
### Include Order
|
|
256
|
+
|
|
257
|
+
```cpp
|
|
258
|
+
// 1. Corresponding header (for .cpp files)
|
|
259
|
+
#include "myproject/user_service.hpp"
|
|
260
|
+
|
|
261
|
+
// 2. C++ standard library
|
|
262
|
+
#include <algorithm>
|
|
263
|
+
#include <string>
|
|
264
|
+
#include <vector>
|
|
265
|
+
|
|
266
|
+
// 3. Third-party libraries
|
|
267
|
+
#include <fmt/core.h>
|
|
268
|
+
#include <spdlog/spdlog.h>
|
|
269
|
+
#include <nlohmann/json.hpp>
|
|
270
|
+
|
|
271
|
+
// 4. Project headers
|
|
272
|
+
#include "myproject/database.hpp"
|
|
273
|
+
#include "myproject/error.hpp"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Logging with spdlog
|
|
279
|
+
|
|
280
|
+
### Setup
|
|
281
|
+
|
|
282
|
+
```cpp
|
|
283
|
+
#include <spdlog/spdlog.h>
|
|
284
|
+
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
285
|
+
#include <spdlog/sinks/rotating_file_sink.h>
|
|
286
|
+
|
|
287
|
+
void init_logging() {
|
|
288
|
+
auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
|
289
|
+
auto file = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
|
|
290
|
+
"logs/app.log", 1024 * 1024 * 5, 3);
|
|
291
|
+
|
|
292
|
+
auto logger = std::make_shared<spdlog::logger>(
|
|
293
|
+
"app", spdlog::sinks_init_list{console, file});
|
|
294
|
+
logger->set_level(spdlog::level::info);
|
|
295
|
+
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v");
|
|
296
|
+
spdlog::set_default_logger(logger);
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Usage
|
|
301
|
+
|
|
302
|
+
```cpp
|
|
303
|
+
// GOOD: Structured key-value logging with fmt syntax
|
|
304
|
+
spdlog::info("user_created id={} email={}", user.id(), user.email());
|
|
305
|
+
spdlog::warn("retry_attempt service={} attempt={}/{}", "payment", attempt, max);
|
|
306
|
+
spdlog::error("operation_failed reason={}", error.message());
|
|
307
|
+
|
|
308
|
+
// BAD: Unstructured string concatenation
|
|
309
|
+
spdlog::info("User " + name + " was created");
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Log Levels
|
|
313
|
+
|
|
314
|
+
| Level | Use Case |
|
|
315
|
+
|---|---|
|
|
316
|
+
| `trace` | Fine-grained debug detail (disabled in production) |
|
|
317
|
+
| `debug` | Development diagnostics |
|
|
318
|
+
| `info` | Normal operations (request handled, service started) |
|
|
319
|
+
| `warn` | Recoverable issues (retry succeeded, deprecated usage) |
|
|
320
|
+
| `error` | Failed operations (connection lost, invalid response) |
|
|
321
|
+
| `critical` | System unusable (out of memory, data corruption) |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Documentation Standards
|
|
326
|
+
|
|
327
|
+
### Doxygen-Style Comments
|
|
328
|
+
|
|
329
|
+
```cpp
|
|
330
|
+
/// @brief Create a new user account.
|
|
331
|
+
///
|
|
332
|
+
/// Validates email uniqueness and hashes the password before
|
|
333
|
+
/// persisting to the database.
|
|
334
|
+
///
|
|
335
|
+
/// @param request Validated user creation request.
|
|
336
|
+
/// @return The created User, or an error if email is taken.
|
|
337
|
+
/// @see UserRepo::save
|
|
338
|
+
[[nodiscard]] Result<User> create_user(const CreateRequest& request);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### When to Write Documentation
|
|
342
|
+
|
|
343
|
+
| Element | Documentation Required? |
|
|
344
|
+
|---|---|
|
|
345
|
+
| Public class | Yes |
|
|
346
|
+
| Public function/method | Yes |
|
|
347
|
+
| Private function | Only if non-obvious |
|
|
348
|
+
| Test functions | No (test name is the doc) |
|
|
349
|
+
| Template parameters | Yes, describe constraints |
|
|
350
|
+
|
|
351
|
+
### Inline Comments
|
|
352
|
+
|
|
353
|
+
```cpp
|
|
354
|
+
// GOOD: Explain WHY, not what
|
|
355
|
+
// Rate limit to 5/min to prevent brute force attacks
|
|
356
|
+
constexpr int kMaxAuthAttemptsPerMinute = 5;
|
|
357
|
+
|
|
358
|
+
// BAD: Restates the code
|
|
359
|
+
// Set max to 5
|
|
360
|
+
constexpr int kMax = 5;
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## PR Review Checklist
|
|
366
|
+
|
|
367
|
+
### Author Checklist (Before Requesting Review)
|
|
368
|
+
|
|
369
|
+
- [ ] Tests pass locally (`ctest --test-dir build`)
|
|
370
|
+
- [ ] clang-tidy passes (`run-clang-tidy -p build`)
|
|
371
|
+
- [ ] clang-format applied (`clang-format -i ...`)
|
|
372
|
+
- [ ] No compiler warnings with `-Wall -Wextra -Werror`
|
|
373
|
+
- [ ] New features have tests
|
|
374
|
+
- [ ] No secrets or credentials committed
|
|
375
|
+
- [ ] PR description explains WHY, not just what
|
|
376
|
+
|
|
377
|
+
### Reviewer Checklist
|
|
378
|
+
|
|
379
|
+
- [ ] Code follows project conventions
|
|
380
|
+
- [ ] Error cases are handled (expected/exceptions)
|
|
381
|
+
- [ ] No raw owning pointers, manual new/delete
|
|
382
|
+
- [ ] No unnecessary copies (use references, string_view, span)
|
|
383
|
+
- [ ] Thread safety considered for shared data
|
|
384
|
+
- [ ] Tests cover happy path and edge cases
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## .gitignore
|
|
389
|
+
|
|
390
|
+
```gitignore
|
|
391
|
+
# Build artifacts
|
|
392
|
+
build/
|
|
393
|
+
build-*/
|
|
394
|
+
out/
|
|
395
|
+
|
|
396
|
+
# IDE
|
|
397
|
+
.vscode/settings.json
|
|
398
|
+
.idea/
|
|
399
|
+
*.swp
|
|
400
|
+
*~
|
|
401
|
+
|
|
402
|
+
# vcpkg
|
|
403
|
+
vcpkg_installed/
|
|
404
|
+
|
|
405
|
+
# Coverage
|
|
406
|
+
*.gcda
|
|
407
|
+
*.gcno
|
|
408
|
+
coverage-report/
|
|
409
|
+
|
|
410
|
+
# Compiled objects
|
|
411
|
+
*.o
|
|
412
|
+
*.obj
|
|
413
|
+
*.a
|
|
414
|
+
*.so
|
|
415
|
+
*.dylib
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
_Conventions reduce cognitive load. Follow them consistently so the team can focus on solving problems, not debating style._
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Error Handling Patterns
|
|
2
|
+
|
|
3
|
+
Structured error handling for modern C++ with std::expected (C++23), exceptions, and RAII.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Fail fast**: Validate inputs early, return errors immediately on invalid state
|
|
10
|
+
- **Make errors visible**: Use types that force callers to handle failure
|
|
11
|
+
- **RAII for cleanup**: Never write manual cleanup code; destructors handle it
|
|
12
|
+
- **Pick one strategy per layer**: Do not mix exceptions and error codes in the same API surface
|
|
13
|
+
- **Ref**: C++ Core Guidelines E.1-E.31
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Exception Safety Guarantees
|
|
18
|
+
|
|
19
|
+
Every function provides one of these guarantees. Document which one.
|
|
20
|
+
|
|
21
|
+
| Guarantee | Description | Example |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| **Nothrow** | Never throws. Marked `noexcept`. | Destructors, swap, move operations |
|
|
24
|
+
| **Strong** | If an exception is thrown, state rolls back to before the call. | Copy-and-swap idiom |
|
|
25
|
+
| **Basic** | If an exception is thrown, no resources leak and invariants hold. Objects may be in a valid but unspecified state. | Most standard library operations |
|
|
26
|
+
|
|
27
|
+
```cpp
|
|
28
|
+
// Nothrow guarantee: destructors, move operations (CG: C.66, C.85)
|
|
29
|
+
class Connection {
|
|
30
|
+
public:
|
|
31
|
+
~Connection() noexcept;
|
|
32
|
+
Connection(Connection&& other) noexcept;
|
|
33
|
+
Connection& operator=(Connection&& other) noexcept;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Strong guarantee via copy-and-swap (CG: C.83)
|
|
37
|
+
class UserList {
|
|
38
|
+
public:
|
|
39
|
+
UserList& operator=(UserList other) noexcept { // Pass by value = copy
|
|
40
|
+
swap(*this, other); // noexcept swap
|
|
41
|
+
return *this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
friend void swap(UserList& a, UserList& b) noexcept {
|
|
45
|
+
using std::swap;
|
|
46
|
+
swap(a.users_, b.users_);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private:
|
|
50
|
+
std::vector<User> users_;
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## std::expected (C++23) -- Preferred for Recoverable Errors
|
|
57
|
+
|
|
58
|
+
`std::expected<T, E>` returns either a value of type `T` or an error of type `E`. No stack unwinding, no hidden control flow.
|
|
59
|
+
|
|
60
|
+
### Error Type Definition
|
|
61
|
+
|
|
62
|
+
```cpp
|
|
63
|
+
// error.hpp
|
|
64
|
+
#include <string>
|
|
65
|
+
#include <expected>
|
|
66
|
+
|
|
67
|
+
enum class ErrorCode {
|
|
68
|
+
kNotFound,
|
|
69
|
+
kAlreadyExists,
|
|
70
|
+
kUnauthorized,
|
|
71
|
+
kValidation,
|
|
72
|
+
kInternal,
|
|
73
|
+
kTimeout,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
struct Error {
|
|
77
|
+
ErrorCode code;
|
|
78
|
+
std::string message;
|
|
79
|
+
std::string context; // Optional: file, function, etc.
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
template <typename T>
|
|
83
|
+
using Result = std::expected<T, Error>;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Basic Usage
|
|
87
|
+
|
|
88
|
+
```cpp
|
|
89
|
+
Result<User> find_user(int user_id) {
|
|
90
|
+
auto row = db_.query_one("SELECT * FROM users WHERE id = ?", user_id);
|
|
91
|
+
if (!row) {
|
|
92
|
+
return std::unexpected(Error{
|
|
93
|
+
.code = ErrorCode::kNotFound,
|
|
94
|
+
.message = fmt::format("User {} not found", user_id),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return User::from_row(*row);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Caller
|
|
101
|
+
auto result = find_user(42);
|
|
102
|
+
if (result) {
|
|
103
|
+
spdlog::info("Found user: {}", result->name());
|
|
104
|
+
} else {
|
|
105
|
+
spdlog::warn("Error: {}", result.error().message);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Monadic Chaining (C++23)
|
|
110
|
+
|
|
111
|
+
```cpp
|
|
112
|
+
// Chain operations that may fail: and_then, transform, or_else
|
|
113
|
+
auto user = find_user(user_id)
|
|
114
|
+
.and_then([](User u) -> Result<User> {
|
|
115
|
+
if (!u.is_active()) {
|
|
116
|
+
return std::unexpected(Error{ErrorCode::kUnauthorized, "User inactive"});
|
|
117
|
+
}
|
|
118
|
+
return u;
|
|
119
|
+
})
|
|
120
|
+
.transform([](User u) {
|
|
121
|
+
return UserResponse{u.name(), u.email()};
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Pre-C++23 Alternative: tl::expected
|
|
126
|
+
|
|
127
|
+
```cpp
|
|
128
|
+
// Use tl::expected if your compiler does not yet support std::expected
|
|
129
|
+
#include <tl/expected.hpp>
|
|
130
|
+
|
|
131
|
+
template <typename T>
|
|
132
|
+
using Result = tl::expected<T, Error>;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## When to Use Each Error Strategy
|
|
138
|
+
|
|
139
|
+
| Strategy | Use Case | Example |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| **std::expected** | Recoverable, expected failures | File not found, validation, business logic |
|
|
142
|
+
| **Exceptions** | Unrecoverable or truly exceptional errors | Out of memory, corrupted state, programmer error |
|
|
143
|
+
| **Error codes (enum)** | C interop, embedded, real-time, no-exception builds | OS APIs, hardware drivers |
|
|
144
|
+
| **std::optional** | "No value" without error details | Cache miss, optional config lookup |
|
|
145
|
+
| **assert / contracts** | Precondition violations in debug builds | `assert(ptr != nullptr)` |
|
|
146
|
+
|
|
147
|
+
### Exceptions: When Appropriate (CG: E.2, E.3)
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
// GOOD: Exception for constructor failure (no return value to use)
|
|
151
|
+
class DatabaseConnection {
|
|
152
|
+
public:
|
|
153
|
+
explicit DatabaseConnection(std::string_view conn_str) {
|
|
154
|
+
handle_ = connect(conn_str);
|
|
155
|
+
if (!handle_) {
|
|
156
|
+
throw std::runtime_error(
|
|
157
|
+
fmt::format("Failed to connect to database: {}", conn_str));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
private:
|
|
161
|
+
ConnectionHandle handle_;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// GOOD: Exception for programming errors (should not happen in correct code)
|
|
165
|
+
void process(std::span<const int> data) {
|
|
166
|
+
if (data.empty()) {
|
|
167
|
+
throw std::invalid_argument("data must not be empty");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## RAII for Cleanup (CG: R.1)
|
|
175
|
+
|
|
176
|
+
```cpp
|
|
177
|
+
// GOOD: RAII handles all cleanup automatically
|
|
178
|
+
void transfer_funds(Account& from, Account& to, int amount) {
|
|
179
|
+
auto transaction = db_.begin_transaction(); // RAII: auto-rollback on exception
|
|
180
|
+
|
|
181
|
+
from.withdraw(amount);
|
|
182
|
+
to.deposit(amount);
|
|
183
|
+
|
|
184
|
+
transaction.commit(); // Explicit commit; destructor rolls back if not called
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// BAD: Manual cleanup -- exception-unsafe
|
|
188
|
+
void transfer_funds_bad(Account& from, Account& to, int amount) {
|
|
189
|
+
auto* txn = db_.begin_transaction_raw();
|
|
190
|
+
from.withdraw(amount); // If this throws, txn is leaked
|
|
191
|
+
to.deposit(amount);
|
|
192
|
+
txn->commit();
|
|
193
|
+
delete txn; // Never reached on exception
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## noexcept Specification (CG: E.12)
|
|
200
|
+
|
|
201
|
+
```cpp
|
|
202
|
+
// GOOD: Mark functions noexcept when they genuinely cannot throw
|
|
203
|
+
void swap(Buffer& other) noexcept;
|
|
204
|
+
Buffer(Buffer&& other) noexcept;
|
|
205
|
+
~Buffer() noexcept; // Destructors are implicitly noexcept
|
|
206
|
+
|
|
207
|
+
// GOOD: Conditional noexcept for templates
|
|
208
|
+
template <typename T>
|
|
209
|
+
void swap(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T>);
|
|
210
|
+
|
|
211
|
+
// BAD: Marking functions noexcept when they might throw
|
|
212
|
+
// If they do throw, std::terminate is called immediately -- no cleanup!
|
|
213
|
+
void parse_config(std::string_view json) noexcept; // JSON parsing can fail!
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Custom Exception Hierarchy (When Using Exceptions)
|
|
219
|
+
|
|
220
|
+
```cpp
|
|
221
|
+
// Base application exception
|
|
222
|
+
class AppError : public std::runtime_error {
|
|
223
|
+
public:
|
|
224
|
+
explicit AppError(ErrorCode code, std::string_view message)
|
|
225
|
+
: std::runtime_error(std::string(message))
|
|
226
|
+
, code_(code) {}
|
|
227
|
+
|
|
228
|
+
[[nodiscard]] ErrorCode code() const noexcept { return code_; }
|
|
229
|
+
|
|
230
|
+
private:
|
|
231
|
+
ErrorCode code_;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
class NotFoundError : public AppError {
|
|
235
|
+
public:
|
|
236
|
+
NotFoundError(std::string_view resource, std::string_view id)
|
|
237
|
+
: AppError(ErrorCode::kNotFound,
|
|
238
|
+
fmt::format("{} not found: {}", resource, id)) {}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
class ValidationError : public AppError {
|
|
242
|
+
public:
|
|
243
|
+
explicit ValidationError(std::string_view message)
|
|
244
|
+
: AppError(ErrorCode::kValidation, message) {}
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Anti-Patterns
|
|
251
|
+
|
|
252
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
253
|
+
|---|---|---|
|
|
254
|
+
| `catch (...)` with no rethrow | Silently swallows all errors including memory corruption | Catch specific types; rethrow unknown |
|
|
255
|
+
| Catching by value | Slices derived exceptions, copies unnecessarily | Catch by `const` reference: `catch (const Error& e)` |
|
|
256
|
+
| Bare `throw;` in wrong context | UB if no active exception | Only use `throw;` inside a catch block |
|
|
257
|
+
| Ignoring return codes | Missed errors propagate silently | Use `[[nodiscard]]` on error-returning functions |
|
|
258
|
+
| Manual cleanup without RAII | Exception-unsafe, leak-prone | Wrap resources in RAII types |
|
|
259
|
+
| `noexcept` on functions that throw | Calls `std::terminate` with no stack unwinding | Only mark truly non-throwing functions |
|
|
260
|
+
| Using error codes where expected fits | Verbose, easy to ignore | Use `std::expected<T, E>` for typed results |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
_Errors are data. Classify them, make them visible in the type system, and let RAII handle cleanup. Never swallow errors silently._
|