Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
Error.hpp
Go to the documentation of this file.
1#pragma once
2
10
11#include <cstdint>
12#include <optional>
13#include <stdexcept>
14#include <string>
15
16namespace icarus {
17
18// =============================================================================
19// Error Severity (for SimulationError / logging)
20// =============================================================================
21
22enum class Severity : uint8_t {
27};
28
29// =============================================================================
30// Simulation Error (structured error for OnError hooks)
31// =============================================================================
32
35 std::string message;
36 std::string component;
37 double time = 0.0;
38};
39
40// =============================================================================
41// Base Exception
42// =============================================================================
43
52class Error : public std::runtime_error {
53 public:
54 explicit Error(const std::string &msg, Severity severity = Severity::ERROR,
55 std::string category = "general")
56 : std::runtime_error("[icarus] " + msg), severity_(severity),
57 category_(std::move(category)) {}
58
59 [[nodiscard]] Severity severity() const { return severity_; }
60 [[nodiscard]] const std::string &category() const { return category_; }
61
63 [[nodiscard]] SimulationError toSimulationError(double time = 0.0,
64 const std::string &component = "") const {
65 return SimulationError{.severity = severity_,
66 .message = what(),
67 .component = component.empty() ? category_ : component,
68 .time = time};
69 }
70
71 protected:
73 std::string category_;
74};
75
76// =============================================================================
77// Signal Errors
78// =============================================================================
79
90
94class SignalError : public Error {
95 public:
96 // Legacy single-string constructor for backward compatibility
97 explicit SignalError(const std::string &msg)
98 : Error("Signal: " + msg, Severity::ERROR, "signal"), kind_(SignalErrorKind::NotFound) {}
99
101 const std::string &detail = "")
102 : Error(FormatMessage(kind, signal_name, detail), KindToSeverity(kind), "signal"),
103 kind_(kind), signal_name_(signal_name) {}
104
105 [[nodiscard]] SignalErrorKind kind() const { return kind_; }
106 [[nodiscard]] const std::string &signal_name() const { return signal_name_; }
107
108 // Convenience factory methods
109 static SignalError NotFound(const std::string &name) {
110 return {SignalErrorKind::NotFound, name};
111 }
112
113 static SignalError Duplicate(const std::string &name, const std::string &existing_owner,
114 const std::string &new_owner) {
115 return {SignalErrorKind::Duplicate, name,
116 "already owned by '" + existing_owner + "', conflict from '" + new_owner + "'"};
117 }
118
119 static SignalError TypeMismatch(const std::string &name, const std::string &expected,
120 const std::string &actual) {
121 return {SignalErrorKind::TypeMismatch, name, "expected " + expected + ", got " + actual};
122 }
123
124 static SignalError Unwired(const std::string &name) { return {SignalErrorKind::Unwired, name}; }
125
126 static SignalError NullPointer(const std::string &name, const std::string &context = "") {
127 return {SignalErrorKind::NullPointer, name, context};
128 }
129
130 private:
131 static Severity KindToSeverity(SignalErrorKind kind) {
132 switch (kind) {
134 return Severity::ERROR;
136 return Severity::ERROR;
138 return Severity::ERROR;
140 return Severity::WARNING; // May be intentional
142 return Severity::FATAL; // Programming error
143 }
144 return Severity::ERROR;
145 }
146
147 static std::string FormatMessage(SignalErrorKind kind, const std::string &name,
148 const std::string &detail) {
149 std::string prefix;
150 switch (kind) {
152 prefix = "Signal not found";
153 break;
155 prefix = "Duplicate signal";
156 break;
158 prefix = "Type mismatch";
159 break;
161 prefix = "Unwired input";
162 break;
164 prefix = "Null pointer";
165 break;
166 }
167 std::string msg = prefix + ": '" + name + "'";
168 if (!detail.empty()) {
169 msg += " (" + detail + ")";
170 }
171 return msg;
172 }
173
174 SignalErrorKind kind_;
175 std::string signal_name_;
176};
177
178// =============================================================================
179// Configuration Errors
180// =============================================================================
181
185class ConfigError : public Error {
186 public:
187 explicit ConfigError(const std::string &msg)
188 : Error("Config: " + msg, Severity::ERROR, "config") {}
189
190 ConfigError(const std::string &component, const std::string &key)
191 : Error("Config: " + component + " missing required key '" + key + "'", Severity::ERROR,
192 "config") {}
193
194 ConfigError(const std::string &message, const std::string &file, int line,
195 const std::string &hint = "")
196 : Error(FormatMessage(message, file, line, hint), Severity::ERROR, "config"), file_(file),
197 line_(line), hint_(hint) {}
198
199 [[nodiscard]] const std::string &file() const { return file_; }
200 [[nodiscard]] int line() const { return line_; }
201 [[nodiscard]] const std::string &hint() const { return hint_; }
202
203 private:
204 static std::string FormatMessage(const std::string &msg, const std::string &file, int line,
205 const std::string &hint) {
206 std::string result = "Config: " + msg;
207 if (!file.empty()) {
208 result += "\n at: " + file;
209 if (line >= 0) {
210 result += ":" + std::to_string(line);
211 }
212 }
213 if (!hint.empty()) {
214 result += "\n hint: " + hint;
215 }
216 return result;
217 }
218
219 std::string file_;
220 int line_ = -1;
221 std::string hint_;
222};
223
224// =============================================================================
225// Lifecycle Errors
226// =============================================================================
227
232
236class LifecycleError : public Error {
237 public:
238 explicit LifecycleError(const std::string &msg)
239 : Error("Lifecycle: " + msg, Severity::ERROR, "lifecycle"), phase_(LifecyclePhase::Other) {}
240
241 LifecycleError(LifecyclePhase phase, const std::string &msg)
242 : Error("Lifecycle [" + PhaseName(phase) + "]: " + msg, Severity::ERROR, "lifecycle"),
243 phase_(phase) {}
244
245 [[nodiscard]] LifecyclePhase phase() const { return phase_; }
246
247 private:
248 static std::string PhaseName(LifecyclePhase phase) {
249 switch (phase) {
251 return "Provision";
253 return "Stage";
255 return "Step";
257 return "Reset";
259 return "Other";
260 }
261 return "Unknown";
262 }
263
264 LifecyclePhase phase_;
265};
266
267// =============================================================================
268// Wiring/Routing Errors
269// =============================================================================
270
274class WiringError : public Error {
275 public:
276 explicit WiringError(const std::string &msg)
277 : Error("Wiring: " + msg, Severity::ERROR, "wiring") {}
278
279 WiringError(const std::string &input, const std::string &output, const std::string &reason)
280 : Error("Wiring: cannot connect '" + input + "' -> '" + output + "': " + reason,
281 Severity::ERROR, "wiring"),
282 input_(input), output_(output) {}
283
284 [[nodiscard]] const std::string &input() const { return input_; }
285 [[nodiscard]] const std::string &output() const { return output_; }
286
287 private:
288 std::string input_;
289 std::string output_;
290};
291
292// =============================================================================
293// Integration/ODE Errors
294// =============================================================================
295
299class IntegrationError : public Error {
300 public:
301 explicit IntegrationError(const std::string &msg)
302 : Error("Integration: " + msg, Severity::ERROR, "integration") {}
303
304 IntegrationError(double t, double dt, const std::string &reason)
305 : Error("Integration failed at t=" + std::to_string(t) + ", dt=" + std::to_string(dt) +
306 ": " + reason,
307 Severity::ERROR, "integration"),
308 t_(t), dt_(dt) {}
309
310 [[nodiscard]] std::optional<double> t() const { return t_; }
311 [[nodiscard]] std::optional<double> dt() const { return dt_; }
312
313 // Convenience for step size errors
314 static IntegrationError StepTooSmall(double min_dt) {
315 return IntegrationError("Step size below minimum: " + std::to_string(min_dt));
316 }
317
318 private:
319 std::optional<double> t_;
320 std::optional<double> dt_;
321};
322
323// =============================================================================
324// State Errors
325// =============================================================================
326
330class StateError : public Error {
331 public:
332 explicit StateError(const std::string &msg)
333 : Error("State: " + msg, Severity::ERROR, "state") {}
334
335 StateError(std::size_t expected, std::size_t actual)
336 : Error("State: size mismatch - expected " + std::to_string(expected) + ", got " +
337 std::to_string(actual),
338 Severity::FATAL, "state"), // Size mismatch is a programming error
339 expected_(expected), actual_(actual) {}
340
341 [[nodiscard]] std::optional<std::size_t> expected() const { return expected_; }
342 [[nodiscard]] std::optional<std::size_t> actual() const { return actual_; }
343
344 private:
345 std::optional<std::size_t> expected_;
346 std::optional<std::size_t> actual_;
347};
348
349// =============================================================================
350// I/O Errors
351// =============================================================================
352
356class IOError : public Error {
357 public:
358 explicit IOError(const std::string &msg) : Error("IO: " + msg, Severity::ERROR, "io") {}
359
360 IOError(const std::string &operation, const std::string &path, const std::string &reason)
361 : Error("IO: " + operation + " '" + path + "': " + reason, Severity::ERROR, "io"),
362 path_(path) {}
363
364 [[nodiscard]] const std::string &path() const { return path_; }
365
366 private:
367 std::string path_;
368};
369
370// =============================================================================
371// Condition Errors
372// =============================================================================
373
377class ConditionError : public Error {
378 public:
379 explicit ConditionError(const std::string &msg)
380 : Error("Condition: " + msg, Severity::ERROR, "condition") {}
381
382 ConditionError(const std::string &condition, const std::string &reason)
383 : Error("Condition: '" + condition + "': " + reason, Severity::ERROR, "condition"),
384 condition_(condition) {}
385
386 [[nodiscard]] const std::string &condition() const { return condition_; }
387
388 private:
389 std::string condition_;
390};
391
392// =============================================================================
393// Not Implemented
394// =============================================================================
395
400 public:
401 explicit NotImplementedError(const std::string &feature)
402 : Error("Not implemented: " + feature, Severity::ERROR, "system") {}
403};
404
405// =============================================================================
406// Legacy Types (for backward compatibility during transition)
407// =============================================================================
408
409// These wrapper classes allow existing code to compile while migrating.
410// They inherit from the consolidated classes for catch compatibility.
411// TODO: Remove after all sites are updated to use base types directly
412
413// Lifecycle phase wrappers
415 public:
416 explicit ProvisionError(const std::string &msg)
418};
419
421 public:
422 explicit StageError(const std::string &msg) : LifecycleError(LifecyclePhase::Stage, msg) {}
423};
424
425class StepError : public LifecycleError {
426 public:
427 explicit StepError(const std::string &msg) : LifecycleError(LifecyclePhase::Step, msg) {}
428};
429
430// Wiring/routing alias
432
433// Config alias
435
436// Integration alias
438
439// Signal error subtypes - must be classes for EXPECT_THROW compatibility
441 public:
442 TypeMismatchError(const std::string &name, const std::string &expected,
443 const std::string &actual)
445 "expected " + expected + ", got " + actual) {}
446};
447
449 public:
450 DuplicateSignalError(const std::string &name, const std::string &existing,
451 const std::string &new_owner)
453 "already owned by '" + existing + "', conflict from '" + new_owner + "'") {}
454};
455
457 public:
458 explicit SignalNotFoundError(const std::string &name)
460};
461
463 public:
464 explicit UnwiredInputError(const std::string &name)
466};
467
468// State error subtypes
470 public:
471 StateSizeMismatchError(std::size_t expected, std::size_t actual)
473};
474
475// Integration error subtypes
477 public:
478 explicit StepSizeTooSmallError(double min_dt)
479 : IntegrationError("Step size below minimum: " + std::to_string(min_dt)) {}
480};
481
482// =============================================================================
483// Throw-and-Log Helpers
484// =============================================================================
485
496template <typename E>
497[[noreturn]] void ThrowError(E &&error, [[maybe_unused]] double time = 0.0,
498 [[maybe_unused]] const std::string &component = "") {
499 // Note: Logging integration happens in ErrorHandler when catching,
500 // or can be done explicitly before throwing via LogService.
501 throw std::forward<E>(error);
502}
503
504} // namespace icarus
505
506// =============================================================================
507// Error Throwing Macros
508// =============================================================================
509
510// NOLINTBEGIN(cppcoreguidelines-macro-usage)
511
515#define ICARUS_THROW(error) throw(error)
516
523#define ICARUS_THROW_CTX(error, time, component) ::icarus::ThrowError((error), (time), (component))
524
525// NOLINTEND(cppcoreguidelines-macro-usage)
const std::string & condition() const
Definition Error.hpp:386
ConditionError(const std::string &condition, const std::string &reason)
Definition Error.hpp:382
ConditionError(const std::string &msg)
Definition Error.hpp:379
Configuration/parsing errors with optional file context.
Definition Error.hpp:185
ConfigError(const std::string &component, const std::string &key)
Definition Error.hpp:190
ConfigError(const std::string &msg)
Definition Error.hpp:187
int line() const
Definition Error.hpp:200
ConfigError(const std::string &message, const std::string &file, int line, const std::string &hint="")
Definition Error.hpp:194
const std::string & hint() const
Definition Error.hpp:201
const std::string & file() const
Definition Error.hpp:199
DuplicateSignalError(const std::string &name, const std::string &existing, const std::string &new_owner)
Definition Error.hpp:450
SimulationError toSimulationError(double time=0.0, const std::string &component="") const
Convert to SimulationError for ErrorHandler integration.
Definition Error.hpp:63
std::string category_
Definition Error.hpp:73
Error(const std::string &msg, Severity severity=Severity::ERROR, std::string category="general")
Definition Error.hpp:54
Severity severity_
Definition Error.hpp:72
const std::string & category() const
Definition Error.hpp:60
Severity severity() const
Definition Error.hpp:59
IOError(const std::string &operation, const std::string &path, const std::string &reason)
Definition Error.hpp:360
const std::string & path() const
Definition Error.hpp:364
IOError(const std::string &msg)
Definition Error.hpp:358
Integration/ODE solver errors with time context.
Definition Error.hpp:299
IntegrationError(const std::string &msg)
Definition Error.hpp:301
static IntegrationError StepTooSmall(double min_dt)
Definition Error.hpp:314
std::optional< double > dt() const
Definition Error.hpp:311
std::optional< double > t() const
Definition Error.hpp:310
IntegrationError(double t, double dt, const std::string &reason)
Definition Error.hpp:304
LifecycleError(const std::string &msg)
Definition Error.hpp:238
LifecycleError(LifecyclePhase phase, const std::string &msg)
Definition Error.hpp:241
LifecyclePhase phase() const
Definition Error.hpp:245
NotImplementedError(const std::string &feature)
Definition Error.hpp:401
ProvisionError(const std::string &msg)
Definition Error.hpp:416
static SignalError NullPointer(const std::string &name, const std::string &context="")
Definition Error.hpp:126
const std::string & signal_name() const
Definition Error.hpp:106
static SignalError Duplicate(const std::string &name, const std::string &existing_owner, const std::string &new_owner)
Definition Error.hpp:113
SignalErrorKind kind() const
Definition Error.hpp:105
SignalError(const std::string &msg)
Definition Error.hpp:97
static SignalError TypeMismatch(const std::string &name, const std::string &expected, const std::string &actual)
Definition Error.hpp:119
SignalError(SignalErrorKind kind, const std::string &signal_name, const std::string &detail="")
Definition Error.hpp:100
static SignalError Unwired(const std::string &name)
Definition Error.hpp:124
static SignalError NotFound(const std::string &name)
Definition Error.hpp:109
SignalNotFoundError(const std::string &name)
Definition Error.hpp:458
StageError(const std::string &msg)
Definition Error.hpp:422
std::optional< std::size_t > actual() const
Definition Error.hpp:342
std::optional< std::size_t > expected() const
Definition Error.hpp:341
StateError(std::size_t expected, std::size_t actual)
Definition Error.hpp:335
StateError(const std::string &msg)
Definition Error.hpp:332
StateSizeMismatchError(std::size_t expected, std::size_t actual)
Definition Error.hpp:471
StepError(const std::string &msg)
Definition Error.hpp:427
StepSizeTooSmallError(double min_dt)
Definition Error.hpp:478
TypeMismatchError(const std::string &name, const std::string &expected, const std::string &actual)
Definition Error.hpp:442
UnwiredInputError(const std::string &name)
Definition Error.hpp:464
Wiring and signal routing errors.
Definition Error.hpp:274
WiringError(const std::string &msg)
Definition Error.hpp:276
WiringError(const std::string &input, const std::string &output, const std::string &reason)
Definition Error.hpp:279
const std::string & input() const
Definition Error.hpp:284
const std::string & output() const
Definition Error.hpp:285
Definition DataDictionary.hpp:24
Definition AggregationTypes.hpp:13
LifecyclePhase
Simulation lifecycle phases.
Definition Error.hpp:231
@ Step
Definition Error.hpp:231
@ Reset
Definition Error.hpp:231
@ Other
Definition Error.hpp:231
@ Stage
Definition Error.hpp:231
@ Provision
Definition Error.hpp:231
const char * to_string(SignalKind kind)
Convert SignalKind to string.
Definition Signal.hpp:86
void ThrowError(E &&error, double time=0.0, const std::string &component="")
Helper to throw an error and optionally log it.
Definition Error.hpp:497
Severity
Definition Error.hpp:22
@ WARNING
Warning (may trigger graceful degradation).
Definition Error.hpp:24
@ FATAL
Fatal (simulation must stop).
Definition Error.hpp:26
@ INFO
Informational (logged, no action).
Definition Error.hpp:23
@ ERROR
Error (simulation may continue with fallback).
Definition Error.hpp:25
SignalErrorKind
Signal-related error categories.
Definition Error.hpp:83
@ NullPointer
Null data pointer provided.
Definition Error.hpp:88
@ TypeMismatch
Type mismatch during resolution.
Definition Error.hpp:86
@ NotFound
Signal does not exist.
Definition Error.hpp:84
@ Unwired
Input not wired to output.
Definition Error.hpp:87
@ Duplicate
Signal already registered.
Definition Error.hpp:85
IntegrationError IntegratorError
Definition Error.hpp:437
WiringError RoutingError
Definition Error.hpp:431
ConfigError ConfigurationError
Definition Error.hpp:434
Definition Error.hpp:33
Severity severity
Definition Error.hpp:34
std::string message
Definition Error.hpp:35
std::string component
Definition Error.hpp:36
double time
Definition Error.hpp:37