The Sparta Modeling Framework
Loading...
Searching...
No Matches
ParameterTree.hpp
1// <ParameterTree.hpp> -*- C++ -*-
2
3#pragma once
4
5#include <vector>
6#include <sstream>
7#include <regex>
8#include <memory>
9#include <string>
10#include <map>
11#include <any>
12
13#include <boost/algorithm/string.hpp>
14
15#include "sparta/utils/Utils.hpp"
20
21namespace sparta
22{
43 {
44 public:
45
61 class Node
62 {
63 public:
64
68 typedef std::vector<std::unique_ptr<Node>> ChildVector;
69
70 private:
71
72 Node* parent_ = nullptr;
73 ParameterTree * tree_ = nullptr;
74 std::string name_;
75 std::string value_;
76 std::string origin_;
77 bool has_value_;
78 ChildVector children_;
79 uint32_t write_count_;
80 mutable uint32_t read_count_ = 0;
81 std::map<std::string, std::any> user_data_;
82
84 class UserDataPrinterBase
85 {
86 public:
87 virtual ~UserDataPrinterBase() = default;
88 virtual void print(const std::string& name, std::any user_data, std::ostream& o, uint32_t indent) const = 0;
89 };
90
92 template <typename T>
93 class UserDataPrinter : public UserDataPrinterBase
94 {
95 public:
96 void print(const std::string& name, std::any user_data, std::ostream& o, uint32_t indent) const override
97 {
98 const T& ud = std::any_cast<const T&>(user_data);
99 for (uint32_t i = 0; i < indent; ++i) {
100 o << " ";
101 }
102 o << name << ": ";
103 print_(o, ud, indent);
104 o << "\n";
105 }
106
107 private:
108 template <typename Value>
109 typename std::enable_if<MetaStruct::is_any_pointer<Value>::value, void>::type
110 print_(std::ostream& o, const Value& val, uint32_t indent) const {
111 if (!val) {
112 o << "nullptr";
113 } else {
114 o << val << " -> ";
115 print_(o, *val, indent);
116 }
117 }
118
119 template <typename Value>
120 typename std::enable_if<!MetaStruct::is_any_pointer<Value>::value, void>::type
121 print_(std::ostream& o, const Value& val, uint32_t indent) const {
122 if constexpr (std::is_same_v<Value, TreeNode::ExtensionsBase>) {
123 if (auto ps = val.getParameters()) {
124 o << val.getClassName() << " extension with parameters:\n";
125 const std::string s = ps->dumpList();
126 std::vector<std::string> lines;
127 boost::split(lines, s, boost::is_any_of("\n"));
128 for (auto & line : lines) {
129 for (uint32_t i = 0; i < indent+2; ++i) {
130 o << " ";
131 }
132 o << line << "\n";
133 }
134 } else {
135 o << "extension without parameters";
136 }
137 } else {
138 o << val;
139 }
140 }
141 };
142
144 std::unordered_map<std::string, std::unique_ptr<UserDataPrinterBase>> user_data_printers_;
145
150 uint32_t required_ = 0;
151
152 public:
153
157 Node() = delete;
158
159 Node(Node* parent, const std::string& name,
160 const std::string& value, const std::string& origin) :
161 parent_(parent),
162 name_(name),
163 value_(value),
164 origin_(origin),
165 has_value_(true),
166 write_count_(1)
167 {;}
168
172 Node(Node* parent, const std::string& name) :
173 parent_(parent),
174 name_(name),
175 value_(),
176 has_value_(false),
177 write_count_(0)
178 {;}
179
185 Node(Node* parent, ParameterTree* tree) :
186 parent_(parent),
187 tree_(tree),
188 name_(""),
189 value_(),
190 has_value_(false),
191 write_count_(0)
192 {;}
193
198 Node(Node* parent, const Node& n) :
199 parent_(parent),
200 name_(n.name_),
201 value_(n.value_),
202 origin_(n.origin_),
203 has_value_(n.has_value_),
204 write_count_(n.write_count_),
205 read_count_(n.read_count_)
206 {
207 for(auto& x : n.children_){
208 children_.emplace_back(new Node(this, *x.get()));
209 }
210 }
211
218 Node& operator= (const Node& n) {
219 // preserve parent_
220 name_ = n.name_;
221 value_ = n.value_;
222 origin_ = n.origin_;
223 has_value_ = n.has_value_;
224 write_count_ = static_cast<decltype(write_count_)>(has_value_);
225 read_count_ = 0;
226 for(auto& x : n.children_){
227 children_.emplace_back(new Node(this, *x.get()));
228 }
229 return *this;
230 }
231
237 void dump(std::ostream& o) const {
238 o << "<VPT Node: \"" << name_ << "\" children:" << children_.size()
239 << " writes:" << write_count_ << " reads:" << read_count_ << " required:" << required_ << ">";
240 }
241
245 const std::string& getName() const { return name_; }
246
250 Node* getParent() { return parent_; }
251
255 Node const * getParent() const { return parent_; }
256
261 Node* p = this;
262 while(p->getParent() != nullptr){
263 p = p->getParent();
264 }
265 sparta_assert(p->getName().size() == 0);
266 return p;
267 }
268
272 Node const * getRoot() const {
273 Node const * p = this;
274 while(p->getParent() != nullptr){
275 p = p->getParent();
276 }
277 sparta_assert(p->getName().size() == 0);
278 return p;
279 }
280
285 return getRoot()->tree_;
286 }
287
291 const ParameterTree* getOwner() const {
292 return getRoot()->tree_;
293 }
294
298 std::string getPath() const {
299 std::stack<const std::string*> names;
300 Node const * n = this;
301 while(n && n->getName().size() > 0){ // Stop at null parent, or root (which has no name)
302 names.push(&n->getName());
303 n = n->getParent();
304 }
305
306 std::stringstream ss;
307 if(names.size() > 0){
308 ss << *names.top();
309 names.pop();
310 while(names.size() > 0){
311 ss << '.' << *names.top();
312 names.pop();
313 }
314 }
315 return ss.str();
316 }
317
321 bool isRoot() const {
322 return name_.size() == 0;
323 }
324
328 void incrementReadCount() const {
329 ++read_count_;
330 }
331
335 void resetReadCount() const {
336 read_count_ = 0;
337 }
338
343 uint32_t getReadCount() const {
344 return read_count_;
345 }
346
352 const std::string& getValue() const {
353 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
355 return value_;
356 }
357
363 const std::string& peekValue() const {
364 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
365 return value_;
366 }
367
372 const std::string& getOrigin() const {
373 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
374 return origin_;
375 }
376
389 template <typename T, typename=typename std::enable_if<!std::is_convertible<T, std::string>::value>::type>
390 T getAs() const {
391 return lexicalCast<T>(getValue());
392 }
393
397 template <typename T, typename=typename std::enable_if<std::is_convertible<T, std::string>::value>::type>
398 const std::string& getAs() const {
399 return getValue();
400 }
401
405 operator std::string () const {
406 return value_;
407 }
408
413 template <typename T>
414 operator T () const {
415 return getAs<T>();
416 }
417
422 template <typename T>
423 bool operator==(const T& rhp) const {
424 return getAs<T>() == rhp;
425 }
426
436 static bool matches(const std::string& pattern, const std::string& other) {
437 std::regex expr(TreeNode::createSearchRegexPattern(pattern));
438 std::smatch what;
439 return std::regex_match(other, what, expr);
440 }
441
449 Node* getChild(const std::string& name) const {
451 throw SpartaException("Cannot call ParameterTree::Node::getChild with a name that is a search pattern: \"")
452 << name << "\". addChild must be used instead";
453 }
454
455 // Always search in reverse-applied order to match on most recent changes first
456 auto itr = children_.rbegin();
457 for(; itr != children_.rend(); ++itr){
458 if(matches((*itr)->getName(), name)){
459 return itr->get();
460 }
461 }
462
463 return nullptr;
464 }
465
476 Node* create(const std::string& path, bool required) {
477 if(path.size() == 0){
478 return this;
479 }
480
481 size_t name_pos = 0;
482 std::string immediate_child_name;
483 immediate_child_name = TreeNode::getNextName(path, name_pos);
484 if(immediate_child_name.size() == 0){
485 // Cannot get parent like this
486 return nullptr; // TEMPORARY behavior. See docstring
487 //throw SpartaException("Virtual parameter path \"") << path
488 // << "\" is invalid because it contains an empty name (between two '.' "
489 // "characters). Parents cannot currently be referenced in the virtual parameter tree";
490 }
491
492 // Get a child of node if one exists with an exact match before any nodes
493 // with wilcards are encountered. If this node name includes a wildcard, a
494 // child will be returned IFF it the pattern string itself matches with the
495 // highest priority child in this node.
496 Node* child = getPriorityChildMatch(immediate_child_name);
497 if(!child){
498 child = addChild(immediate_child_name, required);
499 }
500
501 if(name_pos == std::string::npos){
502 return child;
503 }
504
505 const std::string remainder = path.substr(name_pos);
506 return child->create(remainder, required);
507 }
508
517 Node* getPriorityChildMatch(const std::string& name) const {
518 if(children_.size() == 0){
519 return nullptr;
520 }
521
522 // Becase the name is a pattern, it is impossible to math against other existing
523 // children which are patterns. Therefore, if a pattern child is encountered
524 // which does not exactly match the given name string, nullptr must be returned so
525 // that the caller appends a new node, whih will have the highest priority
526 const bool name_has_wildcard = TreeNode::hasWildcardCharacters(name);
527
528 // Always search in reverse-applied order to match on most recent changes first.
529 //
530 // This is an optimization to prevent nodes from being created needlessly. It tries
531 // to find a matching node while considering wildcards to avoid creating new nodes
532 // in most cases where it is possible.
533 //
534 // Read the rules below to understand what happens. In general, if we don't get an
535 // exact string match with an existing node before hitting another node which is
536 // an exclusive superset or incomplete subset of the 'name' argument's pattern (it
537 // may have no wildcards) then a new node will need to be added at the end of the
538 // children list to specify a value. An existing node cannot be overridden.
539 auto itr = children_.rbegin();
540 for(; itr != children_.rend(); ++itr){
541 if((*itr)->getName() == name){
542 // Found a node with an exact name match (no pattern matching) before
543 // hitting a pattern node. This node can be used to apply a new parameter
544 return itr->get();
545 }
546
547 if(name_has_wildcard){
548 if(TreeNode::hasWildcardCharacters((*itr)->getName())){
549 // Encountered a wildcard node whch was not an exact string match.
550 // No way to tell if the name pattern exactly matches this node's, so
551 // assume it doesn't
552 return nullptr;
553 }else if(matches(name, (*itr)->getName())){
554 // Node has not wildcards but matches the pattern in 'name'. Therfore,
555 // A new parameter with this name would affect this node and more. A new
556 // node will have to be added to override it.
557 return nullptr;
558 }else{
559 // Node has no wildcards and the name agument is a pattern which
560 // does not match it. Therefore, it can be ignored because this 'name'
561 // will not apply
562 continue;
563 }
564 }else if(matches((*itr)->getName(), name)){
565 // Encountered a wildcard node which matches on this name before hitting an
566 // exact match. Therefore, a new node must be created by the caller so that
567 // the parameter being set will affect a subset of this node's pattern
568 return nullptr;
569 }
570 }
571 // No matches or important mismatches
572 return nullptr;
573 }
574
578 Node* addChild(const std::string& name, bool required) {
579 sparta_assert(hasValue() == false,
580 "Cannot add a child to a virtual parameter tree node \"" << name_
581 << "\" since it already has a value: \"" << value_ << "\"");
582 children_.emplace_back(new Node(this, name));
583 if(required){
584 children_.back()->incRequired();
585 }
586 return (children_.back().get());
587 }
588
596 Node& operator[] (const std::string& name) const {
597 Node* child = getChild(name);
598 sparta_assert(child != nullptr,
599 "Node \"" << name_ << "\" has no child named \"" << name << "\"");
600 return *child;
601 }
602
603 Node& operator[] (const char* name) const {
604 return this->operator[](std::string(name));
605 }
606
614 bool hasValue() const {
615 return has_value_;
616 }
617
625 void setValue(const std::string& val, bool required=true, const std::string& origin="") {
626 //sparta_assert(parent_,
627 // "Cannot assign a value to the root node of a virtual parameter tree");
628 //sparta_assert(read_count_ == 0,
629 // "Cannot set(\"" << val << "\") on node \"" << name_ << "\" because it has been read already");
630 value_ = val;
631 origin_ = origin;
632 has_value_ = true;
633 write_count_++;
634 if(required){
635 required_ += 1;
636 }
637 }
638
642 void incRequired() {
643 required_ += 1;
644 }
645
650 void unrequire() {
651 required_ = 0;
652 for(auto & n : children_){
653 n->unrequire();
654 }
655 }
656
662 std::unique_ptr<Node> release() {
663 return parent_->release_(this);
664 }
665
679 bool set(const std::string& path, const std::string& val, bool required, const std::string& origin="") {
680 //sparta_assert(read_count_ == 0,
681 // "Cannot set(\"" << val << "\") on node \"" << name_
682 // << "\" because it has been read already");
683
684 // Set through root of the tree.
685 std::string full_path = getPath();
686 if(full_path.size() > 0 && path.size() > 0){
687 full_path += ".";
688 }
689 full_path += path;
690 return getOwner()->set(full_path, val, required, origin);
691 }
692
699 const std::string& operator= (const std::string& val) {
700 set("", val, true);
701 return val;
702 }
703
709 bool isRequired() const {
710 // Start at beginning. Another, later-written node may override
711 // this node if it has the same patch or a matching pattern.
712 if (getOwner()->isRequired(getPath()))
713 {
714 return true;
715 }
716 return false;
717 }
718
723 uint32_t getRequiredCount() const {
724 return required_;
725 }
726
730 std::vector<Node*> getChildren() {
731 std::vector<Node*> children;
732 for(auto & n : children_){
733 children.push_back(n.get());
734 }
735 return children;
736 }
737
741 std::vector<const Node*> getChildren() const {
742 std::vector<const Node*> children;
743 for(auto & n : children_){
744 children.push_back(n.get());
745 }
746 return children;
747 }
748
753 const std::string & name,
754 std::vector<Node*> & matching_nodes)
755 {
756 if (getName() == name) {
757 matching_nodes.emplace_back(this);
758 return;
759 }
760
761 const auto children = getChildren();
762 for (ParameterTree::Node * child : children) {
763 child->recursFindPTreeNodesNamed(name, matching_nodes);
764 }
765 }
766
771 const std::string & name,
772 std::vector<const Node*> & matching_nodes) const
773 {
774 if (getName() == name) {
775 matching_nodes.emplace_back(this);
776 return;
777 }
778
779 const auto children = getChildren();
780 for (const ParameterTree::Node * child : children) {
781 child->recursFindPTreeNodesNamed(name, matching_nodes);
782 }
783 }
784
788 void recurseVisitLeaves(std::function<void(const Node*)> callback) const {
789 if (children_.empty()) {
790 callback(this);
791 }
792 for (const auto & child : children_) {
793 child->recurseVisitLeaves(callback);
794 }
795 }
796
800 void recursePrint(std::ostream& o, uint32_t indent=0, bool print_user_data=true) const {
801 for(uint32_t i=0; i<indent; ++i){
802 o << " ";
803 }
804 o << name_;
805 if(has_value_){
806 o << " = \"" << value_ << "\" (read " << read_count_ << ", written " << write_count_
807 << ", required " << required_ << ", origin '" << getOrigin() << "')";
808 }
809 o << "\n";
810 if(print_user_data){
811 printUserData(o, indent+2);
812 }
813 for(auto & n : children_){
814 n->recursePrint(o, indent+2, print_user_data);
815 }
816 }
817
821 void printUserData(std::ostream& o, uint32_t indent=0) const {
822 if (user_data_.empty()) {
823 return;
824 }
825
826 for(uint32_t i=0; i<indent; ++i){
827 o << " ";
828 }
829
830 o << "User data (" << getPath() << "):\n";
831 for (const auto & [ud_name, ud_printer] : user_data_printers_) {
832 std::any ud = user_data_.at(ud_name);
833 ud_printer->print(ud_name, ud, o, indent+2);
834 }
835 }
836
849 void appendTree(const Node* ot) {
850 sparta_assert(ot, "Cannot append a null virtual parameter tree");
851 if(ot->getName().size() > 0){
852 // Attach 'ot' node argument as child of this
853 const bool required = false; // The starting node is not required to exist.. only its node children
854 Node* child = create(ot->getName(), required);
855 child->recursAppendTree_(ot);
856 }else{
857 // 'ot' is a root node (no name). Merge it with this.
858 recursAppendTree_(ot);
859 }
860 }
861
866 template <typename T>
867 void setUserData(const std::string & name, const T & user_data) {
868 static_assert(std::is_copy_constructible<T>::value, "std::any only works with copyable types");
869 user_data_[name] = user_data;
870 user_data_printers_[name] = std::make_unique<UserDataPrinter<T>>();
871 }
872
877 template <typename T>
878 void setUserData(const std::string & name, T && user_data) {
879 static_assert(std::is_copy_constructible<T>::value, "std::any only works with copyable types");
880 user_data_[name] = std::move(user_data);
881 user_data_printers_[name] = std::make_unique<UserDataPrinter<T>>();
882 }
883
887 template <typename T>
888 const T & getUserData(const std::string & name) const {
889 constexpr bool must_exist = true;
890 return *tryGetUserData<T>(name, must_exist);
891 }
892
896 template <typename T>
897 T & getUserData(const std::string & name) {
898 constexpr bool must_exist = true;
899 return *tryGetUserData<T>(name, must_exist);
900 }
901
905 template <typename T>
906 const T * tryGetUserData(const std::string & name, bool must_exist = false) const {
907 auto it = user_data_.find(name);
908 if (it == user_data_.end()) {
909 if (must_exist) {
910 throw SpartaException("User data '") << name << "' does not exist for node '"
911 << getPath() << "'";
912 }
913 return nullptr;
914 }
915 return &std::any_cast<const T&>(it->second);
916 }
917
921 template <typename T>
922 T * tryGetUserData(const std::string & name, bool must_exist = false) {
923 auto it = user_data_.find(name);
924 if (it == user_data_.end()) {
925 if (must_exist) {
926 throw SpartaException("User data '") << name << "' does not exist for node '"
927 << getPath() << "'";
928 }
929 return nullptr;
930 }
931 return &std::any_cast<T&>(it->second);
932 }
933
938 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>> & map) const
939 {
940 for (const auto & key : getUserDataKeys()) {
941 if (auto ext = tryGetUserData<std::shared_ptr<TreeNode::ExtensionsBase>>(key)) {
942 map[this][key] = ext->get();
943 }
944 }
945
946 for (auto child : getChildren()) {
947 child->recurseGetAllNodeExtensions(map);
948 }
949 }
950
954 std::set<std::string> getUserDataKeys() const {
955 std::set<std::string> keys;
956 for (const auto & [key, _] : user_data_) {
957 keys.insert(key);
958 }
959 return keys;
960 }
961
965 bool clearUserData(const std::string & name) {
966 user_data_printers_.erase(name);
967 if (user_data_.count(name)) {
968 user_data_.erase(name);
969 return true;
970 }
971 return false;
972 }
973
977 size_t clearUserData() {
978 user_data_printers_.clear();
979 auto sz = user_data_.size();
980 user_data_.clear();
981 return sz;
982 }
983
989 ChildVector::const_reverse_iterator itr_;
990 public:
991 MatchIterator(ChildVector::const_reverse_iterator itr) :
992 itr_(itr)
993 {;}
994
995 MatchIterator(const MatchIterator&) = default;
996
997 MatchIterator& operator=(const MatchIterator&) = default;
998
999 bool operator==(const MatchIterator& rhp) const {
1000 return itr_ == rhp.itr_;
1001 }
1002
1003 bool operator!=(const MatchIterator& rhp) const {
1004 return itr_ != rhp.itr_;
1005 }
1006
1007 void operator++(int) {++itr_;}
1008
1009 void operator++() {++itr_;}
1010
1011 bool matches(const std::string& other) const {
1012 return ParameterTree::Node::matches((*itr_)->getName(), other);
1013 }
1014
1015 const Node* get() const {
1016 return itr_->get();
1017 }
1018
1019 Node* get() {
1020 return itr_->get();
1021 }
1022
1023 const Node* operator->() const {
1024 return itr_->get();
1025 }
1026 };
1027
1028 friend class MatchIterator;
1029
1033 MatchIterator getMatcherBegin() const { return MatchIterator(children_.rbegin()); }
1034
1038 MatchIterator getMatcherEnd() const { return MatchIterator(children_.rend()); }
1039
1040 private:
1041
1046 void recursAppendTree_(const Node* ot) {
1047 // Inherit value. Never invalidate
1048 if(ot->hasValue()){
1049 setValue(ot->peekValue(), ot->getRequiredCount() > 0, ot->getOrigin());
1050 }
1051
1052 // Inherit user data.
1053 for(const auto & [ud_name, ud_value] : ot->user_data_){
1054 user_data_[ud_name] = ud_value;
1055 }
1056
1057 for(auto & child : ot->getChildren()){
1058 // TODO: copy required count instead of just boolean
1059 Node* c = create(child->getName(), child->getRequiredCount() > 0); // Create if needed
1060 c->recursAppendTree_(child);
1061 }
1062 }
1063
1064 std::unique_ptr<Node> release_(Node *node) {
1065 std::unique_ptr<Node> rtn;
1066 auto it = std::find_if(children_.begin(), children_.end(),
1067 [node] (const auto & child) -> bool {
1068 return (child.get() == node);
1069 });
1070 if (it != children_.end()) {
1071 rtn = std::move(*it);
1072 children_.erase(it);
1073 }
1074 return rtn;
1075 }
1076 };
1077
1082 root_(new Node(nullptr, this))
1083 {;}
1084
1085 ParameterTree(const ParameterTree& rhp) :
1086 root_(new Node(nullptr, this))
1087 {
1088 root_->appendTree(rhp.getRoot());
1089 }
1090
1091 ParameterTree& operator=(const ParameterTree& rhp) {
1092 clear();
1093 merge(rhp);
1094 return *this;
1095 }
1096
1097 //bool operator==(const ParameterTree& rhp) const {
1098 //}
1099
1103 virtual ~ParameterTree() {}
1104
1108 void clear() {
1109 root_.reset(new Node(nullptr, this)); // Clear all children
1110 }
1111
1126 bool set(const std::string& path, const std::string& value, bool required, const std::string& origin="") {
1127 Node* n = create(path, false); // inc required after setting value
1128 if(!n){
1129 return false;
1130 }
1131 n->setValue(value, required, origin);
1132
1133 return true;
1134 }
1135
1150 Node* create(const std::string& path, bool required=false) {
1151 if(path.size() == 0){
1152 return getRoot();
1153 }
1154
1155 return getRoot()->create(path, required);
1156 }
1157
1169 const Node& get(const std::string& path) const {
1170 const Node* node = tryGet(path);
1171 if(!node){
1172 throw SpartaException("Unable to find parameter in tree: \"") << path << "\"";
1173 }
1174 return *node;
1175 }
1176
1181 const Node& operator[] (const std::string& name) const {
1182 return get(name);
1183 }
1184
1191 bool hasValue(const std::string& path, const bool must_be_leaf = true) const {
1192 const Node* n = tryGet_(path, must_be_leaf);
1193 return n != nullptr && n->hasValue();
1194 }
1195
1202 bool exists(const std::string& path, const bool must_be_leaf = true) const {
1203 return tryGet_(path, must_be_leaf) != nullptr;
1204 }
1205
1217 uint32_t getUnreadValueNodes(std::vector<const Node*>* nodes) const {
1218 return recursCountUnreadValueNodes_<const Node>(root_.get(), nodes);
1219 }
1220
1232 uint32_t getUnreadValueNodes(std::vector<Node*>* nodes) {
1233 return recursCountUnreadValueNodes_<Node>(root_.get(), nodes);
1234 }
1235
1241 const Node* tryGet(const std::string& path, const bool must_be_leaf = true) const {
1242 return tryGet_(path, must_be_leaf);
1243 }
1244
1250 Node* tryGet(const std::string& path, const bool must_be_leaf = true) {
1251 return tryGet_(path, must_be_leaf);
1252 }
1253
1259 bool isRequired(const std::string& path) const {
1260
1261 if(path.size() == 0){
1262 return root_->getRequiredCount() > 0;
1263 }
1264
1265 std::string immediate_child_name;
1266 size_t name_pos = 0;
1267 immediate_child_name = TreeNode::getNextName(path, name_pos);
1268
1269 if(immediate_child_name.size() == 0){
1270 // Cannot get parent.
1271 throw SpartaException("Parameter ") << path
1272 << " is invalid because it contains an empty name (between two '.' "
1273 "characters). Parents cannot currently be refrenced in the parameter tree";
1274 }
1275
1276 bool match_found = false;
1277 const bool required = recursGetIsRequired_(root_.get(), path, immediate_child_name, name_pos, match_found);
1278 sparta_assert(match_found,
1279 "Asked ParameterTree if path \"" << path << "\" is required but no "
1280 "matching node was found in the ParameterTree");
1281 return required;
1282 }
1283
1289 bool unrequire(const std::string &path) {
1290 Node * node = tryGet_(path, false);
1291 if(nullptr != node) {
1292 node->unrequire();
1293 return true;
1294 }
1295 return false;
1296 }
1297
1306 bool isRead(const std::string& path) const {
1307 if(path.size() == 0){
1308 return root_->hasValue() && root_->getReadCount() > 0;
1309 }
1310
1311 std::string immediate_child_name;
1312 size_t name_pos = 0;
1313 immediate_child_name = TreeNode::getNextName(path, name_pos);
1314
1315 if(immediate_child_name.size() == 0){
1316 // Cannot get parent.
1317 throw SpartaException("Parameter ") << path
1318 << " is invalid because it contains an empty name (between two '.' "
1319 "characters). Parents cannot currently be refrenced in the parameter tree";
1320 }
1321
1322 return recursIsRead_(root_.get(), path, immediate_child_name, name_pos);
1323 }
1324
1325 Node const * getRoot() const { return root_.get(); }
1326
1327 Node * getRoot() { return root_.get(); }
1328
1336 void merge(const ParameterTree& rhp) {
1337 root_->appendTree(rhp.getRoot());
1338 }
1339
1343 void visitLeaves(std::function<void(const Node*)> callback) const {
1344 root_->recurseVisitLeaves(callback);
1345 }
1346
1350 void recursePrint(std::ostream& o, bool print_user_data=true) const {
1351 root_->recursePrint(o, 0, print_user_data); // Begin with 0 indent
1352 }
1353
1357 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>>
1359 {
1360 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>> all_ext_map;
1361 root_->recurseGetAllNodeExtensions(all_ext_map);
1362 return all_ext_map;
1363 }
1364
1365 private:
1366
1371 const Node* tryGet_(const std::string& path, const bool must_be_leaf = true) const {
1372 if(path.size() == 0){
1373 return root_.get();
1374 }
1375
1376 std::string immediate_child_name;
1377 size_t name_pos = 0;
1378 immediate_child_name = TreeNode::getNextName(path, name_pos);
1379
1380 if(immediate_child_name.size() == 0){
1381 // Cannot get parent.
1382 throw SpartaException("Parameter ") << path
1383 << " is invalid because it contains an empty name (between two '.' "
1384 "characters). Parents cannot currently be refrenced in the parameter tree";
1385 }
1386
1387 return recursTryGet_<const Node>(static_cast<const Node*>(root_.get()), path,
1388 immediate_child_name, name_pos, must_be_leaf);
1389 }
1390
1395 Node* tryGet_(const std::string& path, const bool must_be_leaf = true) {
1396 if(path.size() == 0){
1397 return root_.get();
1398 }
1399
1400 std::string immediate_child_name;
1401 size_t name_pos = 0;
1402 immediate_child_name = TreeNode::getNextName(path, name_pos);
1403
1404 if(immediate_child_name.size() == 0){
1405 // Cannot get parent.
1406 throw SpartaException("Parameter ") << path
1407 << " is invalid because it contains an empty name (between two '.' "
1408 "characters). Parents cannot currently be refrenced in the parameter tree";
1409 }
1410
1411 return recursTryGet_(root_.get(), path,
1412 immediate_child_name, name_pos, must_be_leaf);
1413 }
1414
1415 template<typename NodeT>
1416 static NodeT* recursTryGet_(NodeT* node, const std::string& path,
1417 const std::string& match_name,
1418 size_t name_pos, const bool must_be_leaf)
1419 {
1421 "Cannot attempt to read a node with a path containing wildcard "
1422 "characters. A specific node path must be used. Error in \""
1423 << match_name << "\" from \"" << path << "\"");
1424
1425 if(name_pos == std::string::npos){
1426 // End of the search.. No deeper
1427 NodeT* result = nullptr;
1428 NodeT* backup = nullptr; // First match (if is has no value)
1429 auto itr = node->getMatcherBegin();
1430 for(; itr != node->getMatcherEnd(); ++itr){
1431 if(itr.matches(match_name)){
1432 if(itr.get()->hasValue() || !must_be_leaf){
1433 itr.get()->incrementReadCount();
1434 if(!result){
1435 result = itr.get(); // Found it
1436 }
1437 }else if(!backup){
1438 backup = itr.get();
1439 }
1440 }
1441 }
1442 if(result){
1443 return result;
1444 }
1445 return backup; // No match here
1446 }
1447
1448 // Search deeper
1449 std::string immediate_child_name;
1450 immediate_child_name = TreeNode::getNextName(path, name_pos);
1451
1452 if(immediate_child_name.size() == 0){
1453 // Cannot get parent.
1454 throw SpartaException("Parameter ") << path
1455 << " is invalid because it contains an empty name (between two '.' "
1456 "characters). Parents cannot currently be refrenced in the parameter tree";
1457 }
1458
1459 NodeT* result = nullptr;
1460 auto itr = node->getMatcherBegin();
1461 for(; itr != node->getMatcherEnd(); ++itr){
1462 if(itr.matches(match_name)){
1463 NodeT* match = recursTryGet_(itr.get(), path, immediate_child_name, name_pos, must_be_leaf);
1464 if(match && (match->hasValue() || !must_be_leaf)) {
1465 match->incrementReadCount();
1466 if(result == nullptr){
1467 result = match;
1468 }
1469 }
1470 // Keep this match as result and continue iterating to mark all matching nodes as read
1471 }
1472 }
1473 return result;
1474 }
1475
1479 bool recursGetIsRequired_(const Node* node,
1480 const std::string& path,
1481 const std::string& match_name,
1482 size_t name_pos,
1483 bool& found_match) const
1484 {
1485 found_match = false;
1486
1487 if(name_pos == std::string::npos){
1488 // End of the search.. No deeper
1489 auto itr = node->getMatcherBegin();
1490 for(; itr != node->getMatcherEnd(); ++itr){
1491 if(TreeNode::hasWildcardCharacters(match_name)
1492 ? itr->getName() == match_name // Exact name match if match_name has wildcards
1493 : itr.matches(match_name)) // Pattern match if match_name has no wildcards
1494 {
1495 found_match = true;
1496 return itr.get()->getRequiredCount() > 0;
1497 }
1498 }
1499
1500 return false; // dummy
1501 }
1502
1503 // Search deeper
1504 std::string immediate_child_name;
1505 immediate_child_name = TreeNode::getNextName(path, name_pos);
1506
1507 if(immediate_child_name.size() == 0){
1508 // Cannot get parent.
1509 throw SpartaException("Parameter ") << path
1510 << " is invalid because it contains an empty name (between two '.' "
1511 "characters). Parents cannot currently be refrenced in the parameter tree";
1512 }
1513
1514 auto itr = node->getMatcherBegin();
1515 for(; itr != node->getMatcherEnd(); ++itr){
1516 if(itr.matches(match_name)){
1517 const bool required = recursGetIsRequired_(itr.get(), path, immediate_child_name, name_pos, found_match);
1518 if(found_match){
1519 return required;
1520 }
1521 // No match found. Keep going
1522 }
1523 }
1524
1525 sparta_assert(found_match == false); // Should not have been set
1526 return false; // dummy
1527 }
1528
1532 template<class NodeT>
1533 static uint32_t recursCountUnreadValueNodes_(NodeT* n, std::vector<NodeT*> * nodes) {
1534 uint32_t count = 0;
1535 if(n->hasValue() && (n->getReadCount() == 0)){
1536 count = 1;
1537 if(nodes){
1538 nodes->push_back(n);
1539 }
1540 }
1541 auto itr = n->getMatcherBegin();
1542 for(; itr != n->getMatcherEnd(); ++itr){
1543 count += recursCountUnreadValueNodes_<NodeT>(itr.get(), nodes);
1544 }
1545 return count;
1546 }
1547
1551 bool recursIsRead_(const Node* node,
1552 const std::string& path,
1553 const std::string& match_name,
1554 size_t name_pos) const
1555 {
1557 "Cannot attempt to read a node with a path containing wildcard "
1558 "characters. A specific node path must be used. Error in \""
1559 << match_name << "\" from \"" << path << "\"");
1560
1561 if(name_pos == std::string::npos){
1562 auto itr = node->getMatcherBegin();
1563 for(; itr != node->getMatcherEnd(); ++itr){
1564 if(itr.matches(match_name)){
1565 if(itr.get()->hasValue() && itr.get()->getReadCount() > 0){
1566 return true;
1567 }
1568 }
1569 }
1570 return false;
1571 }
1572
1573 // Search deeper
1574 std::string immediate_child_name;
1575 immediate_child_name = TreeNode::getNextName(path, name_pos);
1576 if(immediate_child_name.size() == 0){
1577 // Cannot get parent.
1578 throw SpartaException("Parameter ") << path
1579 << " is invalid because it contains an empty name (between two '.' "
1580 "characters). Parents cannot currently be refrenced in the parameter tree";
1581 }
1582
1583 auto itr = node->getMatcherBegin();
1584 for(; itr != node->getMatcherEnd(); ++itr){
1585 if(itr.matches(match_name)){
1586 bool read = recursIsRead_(itr.get(), path, immediate_child_name, name_pos);
1587 if(read){
1588 return true;
1589 }
1590 }
1591 }
1592 return false;
1593 }
1594
1598 std::unique_ptr<Node> root_;
1599
1600 }; // class ParameterSet
1601
1602 inline std::ostream& operator<<(std::ostream& o, const ParameterTree::Node& n) {
1603 n.dump(o);
1604 return o;
1605 }
1606
1607 inline std::ostream& operator<<(std::ostream& o, const ParameterTree::Node* n) {
1608 if(!n){
1609 o << "<null ParameterTree::Node>";
1610 }else{
1611 o << *n;
1612 }
1613 return o;
1614 }
1615
1616} // namespace sparta
String-to-value helpers and string formatting helpers.
Contains a collection implementation of various compile-time metaprogramming and Type-Detection APIs ...
Individual Parameter interface base class, container class, and global helpers methods.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Basic Node framework in sparta device tree composite pattern.
Node containing a Parameter and value to apply. Can be used to describes a value extracted from the t...
const std::string & peekValue() const
Gets the value of this object as a string.
Node(Node *parent, const std::string &name)
Value-less constructor.
uint32_t getReadCount() const
Gets the number of times this node has been accessed to be read (i.e. with get/tryGet)
const std::string & getValue() const
Gets the value of this object as a string.
std::unique_ptr< Node > release()
Release this node and its children from the tree.
T getAs() const
Gets the value in this object.
Node * getPriorityChildMatch(const std::string &name) const
Attempts to get an immediate child with an exact match for a given name or pattern string....
std::string getPath() const
Gets the path to this node including the root node.
Node(Node *parent, ParameterTree *tree)
Root node constructor. Constructs node pointing to a new tree having no name. Normal nodes to not hav...
void setUserData(const std::string &name, const T &user_data)
Set any named user data (std::any)
MatchIterator getMatcherBegin() const
Get most recent child added.
static bool matches(const std::string &pattern, const std::string &other)
Does a string, name, interpreted as a sparta TreeNode pattern, match another string interpreted as a ...
std::set< std::string > getUserDataKeys() const
Get all user data keys (names).
Node()=delete
Not default-constructable.
Node * getRoot()
Gets the parent of this node.
void recursFindPTreeNodesNamed(const std::string &name, std::vector< const Node * > &matching_nodes) const
Recursively find all nodes that have a given name.
void recurseVisitLeaves(std::function< void(const Node *)> callback) const
Apply the given callback to all leaf nodes.
Node * addChild(const std::string &name, bool required)
void incrementReadCount() const
Increment the read count of this node.
bool operator==(const T &rhp) const
Equality test. Attempts to lexically cast underlying string to requested data-type.
T & getUserData(const std::string &name)
Get any named user data (std::any_cast)
bool isRoot() const
Is this a root node.
size_t clearUserData()
Clear all user data. Returns the number of elements removed.
void resetReadCount() const
Reset the read count back to zero.
Node * create(const std::string &path, bool required)
Get a child for setting a parameter, creating it if needed.
Node & operator[](const std::string &name) const
Gets a child of this node by its name.
Node * getChild(const std::string &name) const
Gets the most recently created child of this node by a concrete child name.
const std::string & getAs() const
getAs template instance for string types (e.g. char[], const char*, std::string)
bool hasValue() const
Does this node have a value written to it which can be accessed through:
const std::string & getName() const
Gets the name of this node.
std::vector< const Node * > getChildren() const
Gets vector of pointers to children of this node.
void dump(std::ostream &o) const
Dumps the content of this node to an ostream on a single line. Does not recurs into children.
ParameterTree * getOwner()
Gets the ParameterTree object that owns this node.
const T * tryGetUserData(const std::string &name, bool must_exist=false) const
Try to get any named user data (std::any_cast)
Node const * getParent() const
Gets the parent of this node.
void recursePrint(std::ostream &o, uint32_t indent=0, bool print_user_data=true) const
Recursively print.
void incRequired()
Increment the required count.
T * tryGetUserData(const std::string &name, bool must_exist=false)
Try to get any named user data (std::any_cast)
bool isRequired() const
Return true if this parameter node is required to exist by the client by 1 or more "set"-ers using th...
std::vector< std::unique_ptr< Node > > ChildVector
Vector of children owned by this node.
std::vector< Node * > getChildren()
Gets vector of pointers to children of this node.
bool set(const std::string &path, const std::string &val, bool required, const std::string &origin="")
Set the string value of a child of this node. Note that this may not affect this node directly becaus...
void appendTree(const Node *ot)
Appends a tree as a child of this node.
const std::string & getOrigin() const
Gets the origin associated with the value at this node.
void setUserData(const std::string &name, T &&user_data)
Set any named user data (std::any)
void setValue(const std::string &val, bool required=true, const std::string &origin="")
Set a value on this node directly.
Node * getParent()
Gets the parent of this node.
uint32_t getRequiredCount() const
Returns the number of times this node has been flagged as required.
void recurseGetAllNodeExtensions(std::map< const Node *, std::map< std::string, const TreeNode::ExtensionsBase * > > &map) const
Get a mapping from Nodes to their extensions recursively.
void printUserData(std::ostream &o, uint32_t indent=0) const
Pretty-print all user data for this node, if any.
Node const * getRoot() const
Gets the parent of this node.
MatchIterator getMatcherEnd() const
Get end of child match iterator (past oldest child added)
bool clearUserData(const std::string &name)
Clear named user data. Returns true if removed, false if not found.
Node & operator=(const Node &n)
Parent-preserving deep-copy assignment operator.
const T & getUserData(const std::string &name) const
Get any named user data (std::any_cast)
void unrequire()
Clear the required count. This is necessary if a parameter is flagged as deprecated or removed in a c...
void recursFindPTreeNodesNamed(const std::string &name, std::vector< Node * > &matching_nodes)
Recursively find all nodes that have a given name.
Node(Node *parent, const Node &n)
Deep-Copy constructor.
const ParameterTree * getOwner() const
Gets the ParameterTree object that owns this node.
Virtual Parameter Tree. This represents a tree of parameters read from some source but does not neces...
Node * create(const std::string &path, bool required=false)
Add a node to the tree, with proper priority.
bool set(const std::string &path, const std::string &value, bool required, const std::string &origin="")
Add a parameter to the tree, replacing any existing parameter.
const Node * tryGet(const std::string &path, const bool must_be_leaf=true) const
Try to get a node if it exists. Returns nullptr it it does not.
bool exists(const std::string &path, const bool must_be_leaf=true) const
Try to check if a node exists.
Node * tryGet(const std::string &path, const bool must_be_leaf=true)
tryGet non-const version
bool unrequire(const std::string &path)
Unrequire a node in the tree.
bool hasValue(const std::string &path, const bool must_be_leaf=true) const
Try to check if a node has value.
uint32_t getUnreadValueNodes(std::vector< Node * > *nodes)
Counts the number of values attached to the parameter tree which have values but have not been read....
virtual ~ParameterTree()
Destructor.
void visitLeaves(std::function< void(const Node *)> callback) const
Apply the given callback to all leaf nodes.
void recursePrint(std::ostream &o, bool print_user_data=true) const
Recursively print.
bool isRequired(const std::string &path) const
Recursively find first leaf node matching this pattern and decide if any node matching that node's pa...
ParameterTree()
Default Constructor.
void clear()
Clear all content from this tree.
uint32_t getUnreadValueNodes(std::vector< const Node * > *nodes) const
Counts the number of values attached to the parameter tree which have values but have not been read....
bool isRead(const std::string &path) const
Has a node with a given path been read.
const Node & operator[](const std::string &name) const
Gets a node form the parameter tree.
const Node & get(const std::string &path) const
Gets a node from the parameter tree while respecting parameter application order. In other words,...
void merge(const ParameterTree &rhp)
Merge this tree with another by applying all of its parameters to this tree. Parameters in the right ...
std::map< const Node *, std::map< std::string, const TreeNode::ExtensionsBase * > > getAllNodeExtensions() const
Get a mapping from Nodes to their extensions.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
static std::string getNextName(const std::string &name, size_t &pos)
Gets the next name between two '.' chars in a string starting at pos.
static bool hasWildcardCharacters(const std::string &name)
Determines if a given node name has any wildcard characters which will be substituted in createSearch...
static std::string createSearchRegexPattern(const std::string &pat)
Compute a regex pattern for a node child path containing any number of wildcard characters (not a dot...
Macros for handling exponential backoff.
T lexicalCast(const std::string &str, uint32_t base=10)
std::ostream & operator<<(std::ostream &o, const SimulationInfo &info)
ostream insertion operator for SimulationInfo