GCC Code Coverage Report


Directory: ./
File: include/na64util/config-helpers.hh
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 116 137 84.7%
Functions: 46 52 88.5%
Branches: 80 94 85.1%

Line Branch Exec Source
1 #include "na64util/exception.hh"
2 #include "na64util/str-fmt.hh"
3
4 #include <cstdio>
5 #include <limits>
6 #include <sstream>
7 #include <string>
8 #include <type_traits>
9 #include <unordered_map>
10 #include <typeinfo>
11 #include <cassert>
12
13 #include <unordered_set>
14 #include <yaml-cpp/exceptions.h>
15 #include <yaml-cpp/yaml.h>
16
17 namespace na64dp {
18 namespace errors {
19
20 /// Root exception class for all the PDef errors
21 class PDefError : public GenericRuntimeError {
22 public:
23 std::list<std::string> path;
24 public:
25 39 PDefError(const char * msg) : GenericRuntimeError(msg) {}
26 };
27
28 /// No item found in section by given key
29 class NoKeyInSection : public PDefError {
30 public:
31 const std::string key;
32 public:
33 NoKeyInSection(const char * key_)
34 : PDefError("Item not found in section")
35 , key(key_)
36 {}
37 };
38
39 /// No item found in section by given key
40 class NoItemInArray : public PDefError {
41 public:
42 const ssize_t key;
43 public:
44 NoItemInArray(ssize_t key_)
45 : PDefError("No item of this number in array")
46 , key(key_)
47 {}
48 };
49
50 /// Basic error thrown by validators
51 class InvalidParameter : public PDefError {
52 public:
53 14 InvalidParameter(const char * msg) : PDefError(msg) {}
54 };
55
56 ///\brief Requested type does not support the value
57 ///
58 /// Thrown when assignment (destination) type is not large enough to keep
59 /// the source value
60 class ParameterTypeOverflow : public InvalidParameter {
61 public:
62 ParameterTypeOverflow(const char * msg) : InvalidParameter(msg) {}
63 };
64
65 /// Basic error thrown by validators if value has permitted type but
66 /// value is prohibited
67 class InvalidParameterValue : public InvalidParameter {
68 public:
69 4 InvalidParameterValue(const char * msg) : InvalidParameter(msg) {}
70 };
71
72 /// Thrown when bad parameter or parameter section or array name is provided
73 class InvalidParameterName : public InvalidParameter {
74 public:
75 InvalidParameterName(const char * msg) : InvalidParameter(msg) {}
76 };
77
78 /// Type cast errors
79 class InvalidParameterType : public InvalidParameter {
80 public:
81 9 InvalidParameterType(const char * msg) : InvalidParameter(msg) {}
82 };
83
84 /// Parameter is not set, but its value requested in runtime context
85 class ParameterValueIsNotSet : public InvalidParameter {
86 public:
87 1 ParameterValueIsNotSet() : InvalidParameter("Parameter value is not set") {}
88 };
89
90 /// Thrown if path expression is invalid
91 class BadPath : public PDefError {
92 public:
93 std::string path;
94 size_t nChar;
95 20 BadPath(const char * msg, size_t nc) : PDefError(msg), nChar(nc) {}
96 };
97
98 /// Thrown in case of inconsistent parameter definition
99 class PDefInconsistency : public PDefError {
100 public:
101 5 PDefInconsistency(const char * msg) : PDefError(msg) {}
102 };
103
104 /// Thrown in case of re-definition already defined node
105 class NodeRedefinition : public PDefInconsistency {
106 public:
107 5 NodeRedefinition(const char * msg) : PDefInconsistency(msg) {}
108 };
109
110 /// Root exception class for all YAML-related errors
111 class GenericYAMLError : public PDefError {
112 public:
113 YAML::Exception origError;
114 public:
115 GenericYAMLError(const YAML::Exception & e, const char * msg)
116 : PDefError(msg)
117 , origError(e)
118 {}
119 };
120
121 } // namespace ::na64dp::error
122 namespace util {
123 class Composer; // fwd
124 namespace pdef {
125
126 //
127 // Runtime parameters path
128
129 /// Returns true if given string can be used as parameter name (scalar,
130 /// section, array, etc)
131 bool validate_parameter_name(const char * nm);
132
133 /// Single path token entry
134 struct PathToken {
135 enum {
136 kUnknown = 0x0,
137
138 kAnyStrKey = 0x2,
139 kStrKey = kAnyStrKey | 0x1,
140
141 kAnyIndex = 0x4,
142 kIntKey = kAnyIndex | 0x1,
143 } type;
144 union {
145 const char * stringKey;
146 ssize_t index;
147 } pl;
148
149 44 PathToken() : type(kUnknown), pl{nullptr} {}
150 36 PathToken(const char * ptr) : type(kStrKey), pl{ptr} {}
151 10 PathToken(ssize_t i) : type(kIntKey), pl{.index=i} {}
152 };
153
154 ///\brief Path to parameter
155 /// Modifies provided string to obtain representation of parameter path
156 ///
157 /// Path examples:
158 /// ""
159 /// "one"
160 /// "one.two"
161 /// "one[0].two.three"
162 /// "one.*.three"
163 /// "one.foo*.bar[34]"
164 /// "one[*].two"
165 class Path : public std::vector<PathToken> {
166 private:
167 /// Null-delimited list of tokens of size `_strBufLen`, may be not allocated
168 char * _strBuf;
169 /// Size of path string buffer
170 size_t _strBufLen;
171 public:
172 Path(const char * strexpr=nullptr);
173 3 Path(const std::string & cppStr) : Path(cppStr.c_str()) {}
174
2/2
✓ Branch 1 taken 1 times.
✓ Branch 5 taken 1 times.
1 Path(const Path & orig) : Path(orig.to_string().c_str()) {}
175 ~Path();
176
177 Path & operator=(const Path &);
178
179 std::string to_string() const;
180
181 operator std::string() const { return to_string(); }
182 };
183
184 //
185 // Scalars
186
187 /// Parameter definition base class
188 struct BasePDef {
189 virtual void set(const YAML::Node &) = 0;
190 8 virtual ~BasePDef() {}
191 };
192
193 /// Common base for scalar parameters
194 class BaseScalar : public BasePDef {
195 private:
196 /// RO attr, C++ RTTI type
197 const std::type_info & _typeInfo;
198 protected:
199 bool _isSet;
200 public:
201 18 BaseScalar(const std::type_info & ti) : _typeInfo(ti), _isSet(false) {}
202 /// Returns ref to C++ RTTI type
203 const std::type_info & type_info() const { return _typeInfo; }
204
205 17 bool is_set() const { return _isSet; }
206
207 template<typename T> void set_as_of_type(const T &);
208 template<typename T> T get_as_of_type() const;
209 };
210
211 template<typename T>
212 class Scalar : public BaseScalar {
213 public:
214 struct iValidator {
215 /// Shall throw exception if parameter is not valid
216 virtual void validate(const T &) const = 0;
217 };
218 protected:
219 std::shared_ptr<iValidator> _validator;
220 T _value;
221 public:
222 36 Scalar() : BaseScalar(typeid(T))
223 36 , _validator(nullptr)
224 36 {}
225
226 3 void set_validator(std::shared_ptr<iValidator> vv) {
227 3 _validator = vv;
228 3 }
229
230 22 void set(const T & v) {
231 22 _value = v;
232
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 4 times.
22 if(_validator) _validator->validate(_value);
233 14 _isSet = true;
234 14 }
235
236 void set(const YAML::Node & yamlNode) {
237 _value = yamlNode.as<T>();
238 if(_validator) _validator->validate(_value);
239 _isSet = true;
240 }
241
242 5 T get() const {
243
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if(!is_set()) throw errors::ParameterValueIsNotSet();
244 4 return _value;
245 }
246 }; // class Scalar
247
248 // TODO: this getters now throw `std::bad_cast` in case of type mismatch. It is
249 // possible, however, to involve a table of permitted conversions to perform
250 // implicit static casts
251
252 template<typename T> void
253 BaseScalar::set_as_of_type(const T & v) {
254 dynamic_cast<Scalar<T>>(*this).set(v);
255 }
256
257 template<typename T> T
258 BaseScalar::get_as_of_type() const {
259 return dynamic_cast<Scalar<T>>(*this).get();
260 }
261
262 //
263 // Basic validators for scalar parameters
264
265 template<typename T>
266 struct RangeValidator : public Scalar<T>::iValidator {
267 T left, right;
268 9 RangeValidator() : left( std::numeric_limits<T>::min())
269 3 , right(std::numeric_limits<T>::max())
270 3 {}
271 7 void validate(const T & v) const override {
272
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
10 if(v >= left && v <= right) return;
273
1/1
✓ Branch 1 taken 4 times.
4 std::ostringstream oss;
274 4 if( left != std::numeric_limits<T>::min()
275
6/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
4 && right != std::numeric_limits<T>::max()
276 ) {
277
1/1
✓ Branch 1 taken 2 times.
2 oss << "value " << v
278
6/6
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
✓ Branch 16 taken 2 times.
2 << " is out of range [" << left << ", " << right << "]";
279
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 } else if( right != std::numeric_limits<T>::max() ) {
280
1/1
✓ Branch 1 taken 1 times.
1 oss << "value " << v
281
3/3
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 7 taken 1 times.
1 << " should be less than or equal to " << right;
282 } else {
283
1/1
✓ Branch 1 taken 1 times.
1 oss << "value " << v
284
3/3
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 7 taken 1 times.
1 << " should be greater than or equal to " << left;
285 }
286
1/1
✓ Branch 2 taken 4 times.
4 throw errors::InvalidParameterValue(oss.str().c_str());
287 4 }
288 };
289
290 template<typename T>
291 struct SetValidator : public Scalar<T>::iValidator {
292 std::unordered_set<T> permittedValues;
293 SetValidator() {}
294
295 void validate(const T & v) const override {
296 auto it = permittedValues.find(v);
297 if(it != permittedValues.end()) return;
298 std::ostringstream oss;
299 if(permittedValues.size() > 6) { // TODO: configurable
300 oss << "Value \"" << v << "\" is not recognized.";
301 } else {
302 for(const auto & pv : permittedValues) {
303 std::set<std::string> sorted( permittedValues.begin()
304 , permittedValues.end()
305 );
306 oss << "Value \"" << v << "\" is not one of the permitted options: "
307 << str_join(sorted.begin(), sorted.end());
308 }
309 }
310 throw errors::InvalidParameterValue(oss.str().c_str());
311 }
312 };
313
314 // TODO: regex validator
315 // TODO: non-zero (single prohibited value) validator
316
317 //
318 // Parameter tree node
319
320 /// Node of parameter definition tree
321 class Node {
322 public:
323 /// This node description (public RW)
324 std::string description;
325 /// Node-section type
326 typedef std::unordered_map<std::string, std::shared_ptr<Node>> Section;
327 /// Node-array type; can have homogeneous topology
328 struct Array : public std::vector<std::shared_ptr<Node>> {
329 std::shared_ptr<Node> topology; ///< desired topology for array, can be null
330 };
331 private:
332 // this is not exposed to parent scope, should be steered by this class only
333 /// This node type
334 enum {
335 kUnset = 0, ///< empty node, no value type defined
336 kScalar, ///< node contains some kind of scalar value
337 kArray, ///< list/array node
338 kSection, ///< (sub-)section node
339 kUnion, ///< union type
340 kCustom ///< node with custom parameter
341 } _type;
342 /// Node payload
343 union {
344 util::pdef::BasePDef * parameterPtr;
345 Section * section;
346 Array * array;
347 } _pl;
348 public:
349 48 Node() : _type(kUnset)
350 24 , _pl{nullptr}
351 24 {}
352
353 7 bool is_unset() const { return _type == kUnset; }
354 15 bool is_section() const { return _type == kSection; }
355 8 bool is_scalar() const { return _type == kScalar; }
356 9 bool is_array() const { return _type == kArray; }
357 bool is_union() const { return _type == kUnion; }
358 4 bool is_custom() const { return _type == kCustom; }
359
360 const char * node_type_name() const;
361
362 ///\brief Marks unset node as scalar of given type
363 ///
364 ///\throws errors::NodeRedefinition if node already has type
365 template<typename T>
366 36 pdef::Scalar<T> & set_scalar() {
367
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
36 if(_type != kUnset) throw errors::NodeRedefinition("Node type has been set already");
368 30 _type = kScalar;
369 30 _pl.parameterPtr = new Scalar<T>();
370 30 return *static_cast<pdef::Scalar<T>*>(_pl.parameterPtr);
371 }
372 ///\brief Marks node as section node
373 ///
374 ///\throws errors::NodeRedefinition if node already has type
375 Section & set_section();
376 ///\brief Marks node as section node
377 ///
378 ///\throws errors::NodeRedefinition if node already has type
379 Array & set_array();
380 // TODO: ?
381 //\brief Marks node as union node
382 //
383 //\throws errors::NodeRedefinition if node already has type
384 //Union & set_union();
385
386 BaseScalar & as_scalar();
387 const BaseScalar & as_scalar() const;
388 5 template<typename T> Scalar<T> & as_scalar_of_type() {
389 5 auto & bs = as_scalar();
390
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 return dynamic_cast<Scalar<T>&>(bs);
391 }
392 8 template<typename T> const Scalar<T> & as_scalar_of_type() const {
393 8 auto & bs = as_scalar();
394
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 return dynamic_cast<const Scalar<T>&>(bs);
395 }
396
397 Section & as_section();
398 const Section & as_section() const;
399
400 Array & as_array();
401 const Array & as_array() const;
402
403 // section getter shortcuts
404 Node & operator[](const std::string &);
405 const Node & operator[](const std::string &) const;
406 bool has(const std::string &) const;
407
408 // array getter shortcuts
409 Node & operator[](ssize_t);
410 const Node & operator[](ssize_t) const;
411 bool has(ssize_t) const;
412 };
413
414 #if 1
415 class Composer {
416 private:
417 struct NodeEntry {
418 Node & node;
419 std::string name;
420 size_t index;
421 std::shared_ptr<Node> _lastNode;
422 };
423 std::vector<NodeEntry> _stack;
424 protected:
425 void _assure_array();
426 void _assure_section();
427 public:
428 Composer() = delete;
429 Composer(Node & node);
430
431 /// Defines required scalar named parameter (attribute), with name and
432 /// optional description
433 template<typename T>
434 8 Composer & p( const char * name
435 , const char * description=nullptr
436 ) {
437
1/1
✓ Branch 1 taken 4 times.
8 _assure_section();
438
2/3
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
8 if(!validate_parameter_name(name))
439 throw errors::InvalidParameterName("Invalid parameter name.");
440
441
1/1
✓ Branch 1 taken 4 times.
8 auto nodePtr = std::make_shared<Node>();
442
1/1
✓ Branch 2 taken 4 times.
8 nodePtr->set_scalar<T>();
443
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 if(description)
444
1/1
✓ Branch 2 taken 4 times.
8 nodePtr->description = description;
445
2/2
✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
8 _stack.back().node.as_section().emplace(name, nodePtr);
446 8 _stack.back()._lastNode = nodePtr;
447 8 return *this;
448 8 }
449
450 /// Defines required scalar named parameter (attribute), with name,
451 /// validator and optional description
452 template<typename T>
453 Composer & p( const char * name
454 , std::shared_ptr<typename Scalar<T>::iValidator> & validator
455 , const char * description=nullptr
456 ) {
457 _assure_section();
458 if(!validate_parameter_name(name))
459 throw errors::InvalidParameterName("Invalid parameter name.");
460
461 auto nodePtr = std::make_shared<Node>();
462 nodePtr->set_scalar<T>().set_validator(validator);
463 if(description)
464 nodePtr->description = description;
465 _stack.back().node.as_section().emplace(name, nodePtr);
466 _stack.back()._lastNode = nodePtr;
467 return *this;
468 }
469
470 /// Defines (optional) scalar named parameter (attribute), with name,
471 /// default value and optional description
472 template<typename T>
473 6 Composer & p( const char * name
474 , T dftVal
475 , const char * description=nullptr
476 ) {
477
1/1
✓ Branch 1 taken 3 times.
6 _assure_section();
478
2/3
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
6 if(!validate_parameter_name(name))
479 throw errors::InvalidParameterName("Invalid parameter name.");
480
481
1/1
✓ Branch 1 taken 3 times.
6 auto nodePtr = std::make_shared<Node>();
482
2/2
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
6 nodePtr->set_scalar<T>().set(dftVal);
483
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 if(description)
484
1/1
✓ Branch 2 taken 3 times.
6 nodePtr->description = description;
485
2/2
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
6 _stack.back().node.as_section().emplace(name, nodePtr);
486 6 _stack.back()._lastNode = nodePtr;
487 6 return *this;
488 6 }
489
490 /// Defines (optional) scalar named parameter (attribute), with name,
491 /// validator, default value and optional description
492 template<typename T>
493 Composer & p( const char * name
494 , std::shared_ptr<typename Scalar<T>::iValidator> & validator
495 , T dftVal
496 , const char * description=nullptr
497 ) {
498 _assure_section();
499 if(!validate_parameter_name(name))
500 throw errors::InvalidParameterName("Invalid parameter name.");
501
502 auto nodePtr = std::make_shared<Node>();
503 auto & s = nodePtr->set_scalar<T>();
504 s.set_validator(validator);
505 s.set(dftVal);
506 if(description)
507 nodePtr->description = description;
508 _stack.back().node.as_section().emplace(name, nodePtr);
509 _stack.back()._lastNode = nodePtr;
510 return *this;
511 }
512
513 /// Defines required scalar parameter element, with optional description
514 template<typename T>
515 4 Composer & el( const char * description=nullptr ) {
516
1/1
✓ Branch 1 taken 2 times.
4 _assure_array();
517
1/1
✓ Branch 1 taken 2 times.
4 auto nodePtr = std::make_shared<Node>();
518
1/1
✓ Branch 2 taken 2 times.
4 nodePtr->set_scalar<T>();
519
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 if(description)
520
1/1
✓ Branch 2 taken 2 times.
4 nodePtr->description = description;
521
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
4 _stack.back().node.as_array().push_back(nodePtr);
522 4 _stack.back()._lastNode = nodePtr;
523 4 return *this;
524 4 }
525
526 /// Defines attribute sub-node (section or array)
527 1 Composer & bgn_sub( const char * name
528 , const char * description=nullptr
529 ) {
530
1/1
✓ Branch 1 taken 1 times.
1 _assure_section();
531
2/3
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if(!validate_parameter_name(name))
532 throw errors::InvalidParameterName("Invalid subsection name.");
533
534
1/1
✓ Branch 1 taken 1 times.
1 auto nodePtr = std::make_shared<Node>();
535
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(description)
536
1/1
✓ Branch 2 taken 1 times.
1 nodePtr->description = description;
537
2/2
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
1 _stack.back().node.as_section().emplace(name, nodePtr);
538 1 _stack.back()._lastNode = nodePtr;
539
2/3
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 10 not taken.
2 _stack.push_back(NodeEntry{*nodePtr, name, 0});
540 1 return *this;
541 1 }
542
543 /// Finalizes attribute sub-node (section or array)
544 1 Composer & end_sub( const char * name=nullptr ) {
545
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(name) {
546
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(_stack.size() < 2) {
547 throw errors::InvalidParameterName(format("No "
548 " opened subsections to finalize \"%s\""
549 , name).c_str());
550 }
551 1 auto it = _stack.rbegin();
552
3/4
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if(name != it->name) {
553 throw errors::InvalidParameterName(format("Finilizing"
554 " subsection \"%s\" while lates open is \"%s\""
555 , name, it->name.c_str()
556 ).c_str());
557 }
558 }
559 1 _stack.pop_back();
560 1 return *this;
561 }
562
563 #if 0
564 /// Defines required parameter with validator, name and description
565 template<typename T>
566 Composer & p( const char * name
567 , std::shared_ptr<typename pdef::Scalar<T>::iValidator> & validator
568 , const char * description=nullptr
569 ) {
570 if(_lastNode) _finalize_last_node();
571 assert(nullptr == _lastNode);
572 _lastNodeName = name;
573 _lastNode = new pdef::Node(description);
574 auto pp = _lastNode->_set_scalar<T>();
575 pp->set_validator(validator);
576 _lastNode->_set_scalar(pp);
577 return *this;
578 }
579 /// Defines parameter with default value (optional), with name and description
580 template<typename T>
581 Composer & p( const char * name
582 , const T & dft
583 , const char * description=nullptr
584 ) {
585 if(_lastNode) _finalize_last_node();
586 assert(nullptr == _lastNode);
587 _lastNodeName = name;
588 _lastNode = new pdef::Node(description);
589 auto pp = _lastNode->_set_scalar<T>();
590 pp->set(dft);
591 return *this;
592 }
593 /// Defines parameter with default value and validator, with name and description
594 template<typename T>
595 Composer & p( const char * name
596 , const T & dft
597 , std::shared_ptr<typename pdef::Scalar<T>::iValidator> & validator
598 , const char * description=nullptr
599 ) {
600 if(_lastNode) _finalize_last_node();
601 assert(nullptr == _lastNode);
602 _lastNodeName = name;
603 _lastNode = new pdef::Node(description);
604 auto pp = _lastNode->_set_scalar<T>();
605 pp->set_validator(validator);
606 pp->set(dft);
607 return *this;
608 }
609
610 //RuntimeCfg & start_section(const char * name, const char * description=nullptr);
611 //RuntimeCfg & end_section(const char * name=nullptr);
612
613 //void set(const YAML::Node &);
614
615 /// Returns node by path string
616 pdef::Node & get(const char * path);
617
618 /// Returns node by path string, const version
619 const pdef::Node & get(const char *) const;
620 #endif
621 };
622 #endif
623
624 } // namespace ::na64dp::util::pdef
625 } // namespace ::na64dp::util
626 } // namespace na64dp
627
628