Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
ComponentConfig.hpp
Go to the documentation of this file.
1#pragma once
2
10
12#include <icarus/core/Error.hpp>
13
14#include <limits>
15#include <string>
16#include <unordered_map>
17#include <vector>
18
19namespace icarus {
20
38 // =========================================================================
39 // Identity (set by loader/factory)
40 // =========================================================================
41
42 std::string name;
43 std::string entity;
44 std::string type;
45
46 // =========================================================================
47 // Raw Storage (populated by loader)
48 // =========================================================================
49
50 std::unordered_map<std::string, double> scalars;
51 std::unordered_map<std::string, std::vector<double>> vectors;
52 std::unordered_map<std::string, std::vector<double>> arrays;
53 std::unordered_map<std::string, std::string> strings;
54 std::unordered_map<std::string, int64_t> integers;
55 std::unordered_map<std::string, bool> booleans;
56
58 std::vector<std::string> sources;
59
61 std::vector<std::string> body_sources;
62 std::vector<std::string> ecef_sources;
63
64 // =========================================================================
65 // Typed Accessors
66 // =========================================================================
67
75 template <typename T> T Require(const std::string &key) const;
76
84 template <typename T> T Get(const std::string &key, const T &default_value) const;
85
92 template <typename T> [[nodiscard]] bool Has(const std::string &key) const;
93
97 [[nodiscard]] std::string FullPath() const { return MakeFullPath(entity, name); }
98};
99
100// =============================================================================
101// Template Specializations - double
102// =============================================================================
103
104template <>
105inline double ComponentConfig::Get<double>(const std::string &key, const double &def) const {
106 auto it = scalars.find(key);
107 return (it != scalars.end()) ? it->second : def;
108}
109
110template <> inline double ComponentConfig::Require<double>(const std::string &key) const {
111 auto it = scalars.find(key);
112 if (it == scalars.end()) {
113 throw ConfigError("Component '" + FullPath() + "' missing required scalar: " + key);
114 }
115 return it->second;
116}
117
118template <> inline bool ComponentConfig::Has<double>(const std::string &key) const {
119 return scalars.count(key) > 0;
120}
121
122// =============================================================================
123// Template Specializations - int / int64_t
124// =============================================================================
125
126template <> inline int ComponentConfig::Get<int>(const std::string &key, const int &def) const {
127 auto it = integers.find(key);
128 if (it == integers.end()) {
129 return def;
130 }
131 // Range check to prevent silent truncation
132 int64_t val = it->second;
133 if (val < static_cast<int64_t>(std::numeric_limits<int>::min()) ||
134 val > static_cast<int64_t>(std::numeric_limits<int>::max())) {
135 throw ConfigError("Component '" + FullPath() + "' integer '" + key + "' value " +
136 std::to_string(val) + " overflows int range [" +
137 std::to_string(std::numeric_limits<int>::min()) + ", " +
138 std::to_string(std::numeric_limits<int>::max()) + "]");
139 }
140 return static_cast<int>(val);
141}
142
143template <> inline int ComponentConfig::Require<int>(const std::string &key) const {
144 auto it = integers.find(key);
145 if (it == integers.end()) {
146 throw ConfigError("Component '" + FullPath() + "' missing required integer: " + key);
147 }
148 // Range check to prevent silent truncation
149 int64_t val = it->second;
150 if (val < static_cast<int64_t>(std::numeric_limits<int>::min()) ||
151 val > static_cast<int64_t>(std::numeric_limits<int>::max())) {
152 throw ConfigError("Component '" + FullPath() + "' integer '" + key + "' value " +
153 std::to_string(val) + " overflows int range [" +
154 std::to_string(std::numeric_limits<int>::min()) + ", " +
155 std::to_string(std::numeric_limits<int>::max()) + "]");
156 }
157 return static_cast<int>(val);
158}
159
160template <> inline bool ComponentConfig::Has<int>(const std::string &key) const {
161 return integers.count(key) > 0;
162}
163
164template <>
165inline int64_t ComponentConfig::Get<int64_t>(const std::string &key, const int64_t &def) const {
166 auto it = integers.find(key);
167 return (it != integers.end()) ? it->second : def;
168}
169
170template <> inline int64_t ComponentConfig::Require<int64_t>(const std::string &key) const {
171 auto it = integers.find(key);
172 if (it == integers.end()) {
173 throw ConfigError("Component '" + FullPath() + "' missing required integer: " + key);
174 }
175 return it->second;
176}
177
178template <> inline bool ComponentConfig::Has<int64_t>(const std::string &key) const {
179 return integers.count(key) > 0;
180}
181
182// =============================================================================
183// Template Specializations - bool
184// =============================================================================
185
186template <> inline bool ComponentConfig::Get<bool>(const std::string &key, const bool &def) const {
187 auto it = booleans.find(key);
188 return (it != booleans.end()) ? it->second : def;
189}
190
191template <>
193 const std::string &key) const { // NOLINT(readability-identifier-naming)
194 auto it = booleans.find(key);
195 if (it == booleans.end()) {
196 throw ConfigError("Component '" + FullPath() + "' missing required boolean: " + key);
197 }
198 return it->second;
199}
200
201template <> inline bool ComponentConfig::Has<bool>(const std::string &key) const {
202 return booleans.count(key) > 0;
203}
204
205// =============================================================================
206// Template Specializations - std::string
207// =============================================================================
208
209template <>
210inline std::string ComponentConfig::Get<std::string>(const std::string &key,
211 const std::string &def) const {
212 auto it = strings.find(key);
213 return (it != strings.end()) ? it->second : def;
214}
215
216template <> inline std::string ComponentConfig::Require<std::string>(const std::string &key) const {
217 auto it = strings.find(key);
218 if (it == strings.end()) {
219 throw ConfigError("Component '" + FullPath() + "' missing required string: " + key);
220 }
221 return it->second;
222}
223
224template <> inline bool ComponentConfig::Has<std::string>(const std::string &key) const {
225 return strings.count(key) > 0;
226}
227
228// =============================================================================
229// Template Specializations - Vec3<double>
230// =============================================================================
231
232template <>
233inline Vec3<double> ComponentConfig::Get<Vec3<double>>(const std::string &key,
234 const Vec3<double> &def) const {
235 auto it = vectors.find(key);
236 if (it == vectors.end() || it->second.size() != 3) {
237 return def;
238 }
239 return Vec3<double>{it->second[0], it->second[1], it->second[2]};
240}
241
242template <>
243inline Vec3<double> ComponentConfig::Require<Vec3<double>>(const std::string &key) const {
244 auto it = vectors.find(key);
245 if (it == vectors.end()) {
246 throw ConfigError("Component '" + FullPath() + "' missing required vector: " + key);
247 }
248 if (it->second.size() != 3) {
249 throw ConfigError("Component '" + FullPath() + "' vector '" + key +
250 "' must have exactly 3 elements, got " +
251 std::to_string(it->second.size()));
252 }
253 return Vec3<double>{it->second[0], it->second[1], it->second[2]};
254}
255
256template <> inline bool ComponentConfig::Has<Vec3<double>>(const std::string &key) const {
257 auto it = vectors.find(key);
258 return it != vectors.end() && it->second.size() == 3;
259}
260
261// =============================================================================
262// Template Specializations - Vec4<double> (Quaternion stored as [w,x,y,z])
263// =============================================================================
264
265template <>
266inline Vec4<double> ComponentConfig::Get<Vec4<double>>(const std::string &key,
267 const Vec4<double> &def) const {
268 auto it = vectors.find(key);
269 if (it == vectors.end() || it->second.size() != 4) {
270 return def;
271 }
272 return Vec4<double>{it->second[0], it->second[1], it->second[2], it->second[3]};
273}
274
275template <>
276inline Vec4<double> ComponentConfig::Require<Vec4<double>>(const std::string &key) const {
277 auto it = vectors.find(key);
278 if (it == vectors.end()) {
279 throw ConfigError("Component '" + FullPath() +
280 "' missing required quaternion/vec4: " + key);
281 }
282 if (it->second.size() != 4) {
283 throw ConfigError("Component '" + FullPath() + "' quaternion/vec4 '" + key +
284 "' must have exactly 4 elements, got " +
285 std::to_string(it->second.size()));
286 }
287 return Vec4<double>{it->second[0], it->second[1], it->second[2], it->second[3]};
288}
289
290template <> inline bool ComponentConfig::Has<Vec4<double>>(const std::string &key) const {
291 auto it = vectors.find(key);
292 return it != vectors.end() && it->second.size() == 4;
293}
294
295// =============================================================================
296// Template Specializations - std::vector<double>
297// =============================================================================
298
299template <>
300inline std::vector<double>
301ComponentConfig::Get<std::vector<double>>(const std::string &key,
302 const std::vector<double> &def) const {
303 auto it = arrays.find(key);
304 if (it != arrays.end()) {
305 return it->second;
306 }
307 // Also check vectors (arrays and vectors use same underlying storage for flexibility)
308 auto vit = vectors.find(key);
309 return (vit != vectors.end()) ? vit->second : def;
310}
311
312template <>
313inline std::vector<double>
314ComponentConfig::Require<std::vector<double>>(const std::string &key) const {
315 auto it = arrays.find(key);
316 if (it != arrays.end()) {
317 return it->second;
318 }
319 auto vit = vectors.find(key);
320 if (vit != vectors.end()) {
321 return vit->second;
322 }
323 throw ConfigError("Component '" + FullPath() + "' missing required array: " + key);
324}
325
326template <> inline bool ComponentConfig::Has<std::vector<double>>(const std::string &key) const {
327 return arrays.count(key) > 0 || vectors.count(key) > 0;
328}
329
330// =============================================================================
331// Template Specializations - std::vector<std::string>
332// =============================================================================
333
334template <>
335inline std::vector<std::string>
337 const std::vector<std::string> &def) const {
338 // Special case: known string list members
339 if (key == "sources") {
340 return sources.empty() ? def : sources;
341 }
342 if (key == "body_sources") {
343 return body_sources.empty() ? def : body_sources;
344 }
345 if (key == "ecef_sources") {
346 return ecef_sources.empty() ? def : ecef_sources;
347 }
348 return def;
349}
350
351template <>
352inline std::vector<std::string>
353ComponentConfig::Require<std::vector<std::string>>(const std::string &key) const {
354 if (key == "sources") {
355 if (sources.empty()) {
356 throw ConfigError("Component '" + FullPath() + "' missing required sources list");
357 }
358 return sources;
359 }
360 if (key == "body_sources") {
361 if (body_sources.empty()) {
362 throw ConfigError("Component '" + FullPath() + "' missing required body_sources list");
363 }
364 return body_sources;
365 }
366 if (key == "ecef_sources") {
367 if (ecef_sources.empty()) {
368 throw ConfigError("Component '" + FullPath() + "' missing required ecef_sources list");
369 }
370 return ecef_sources;
371 }
372 throw ConfigError("Component '" + FullPath() + "' missing required string list: " + key);
373}
374
375template <>
376inline bool ComponentConfig::Has<std::vector<std::string>>(const std::string &key) const {
377 if (key == "sources") {
378 return !sources.empty();
379 }
380 if (key == "body_sources") {
381 return !body_sources.empty();
382 }
383 if (key == "ecef_sources") {
384 return !ecef_sources.empty();
385 }
386 return false;
387}
388
389} // namespace icarus
Core type definitions, concepts, and configuration for Icarus.
Consolidated error handling for Icarus.
Configuration/parsing errors with optional file context.
Definition Error.hpp:185
Definition AggregationTypes.hpp:13
std::string MakeFullPath(const std::string &entity, const std::string &name)
Build a full path from entity and name.
Definition CoreTypes.hpp:166
Configuration container for components.
Definition ComponentConfig.hpp:37
std::string entity
Entity namespace (optional).
Definition ComponentConfig.hpp:43
T Get(const std::string &key, const T &default_value) const
Get an optional config value with default.
std::string type
Component type name.
Definition ComponentConfig.hpp:44
std::string FullPath() const
Get the full component path: entity.name (or just name if no entity).
Definition ComponentConfig.hpp:97
std::unordered_map< std::string, int64_t > integers
Definition ComponentConfig.hpp:54
std::unordered_map< std::string, bool > booleans
Definition ComponentConfig.hpp:55
bool Has(const std::string &key) const
Check if a key exists.
std::unordered_map< std::string, std::string > strings
Definition ComponentConfig.hpp:53
T Require(const std::string &key) const
Get a required config value (throws if missing).
std::string name
Component instance name.
Definition ComponentConfig.hpp:42
std::unordered_map< std::string, std::vector< double > > vectors
Vec3 stored as [x,y,z].
Definition ComponentConfig.hpp:51
std::vector< std::string > body_sources
Frame-categorized source lists (for ForceAggregator frame transformations).
Definition ComponentConfig.hpp:61
std::unordered_map< std::string, double > scalars
Definition ComponentConfig.hpp:50
std::vector< std::string > sources
List-based config (for aggregators with variable source counts, etc.).
Definition ComponentConfig.hpp:58
std::unordered_map< std::string, std::vector< double > > arrays
Definition ComponentConfig.hpp:52
std::vector< std::string > ecef_sources
Sources outputting in ECEF frame.
Definition ComponentConfig.hpp:62