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 if constexpr (std::is_same_v<Value, bool>) {
138 o << std::boolalpha << val << std::dec;
139 } else {
140 o << val;
141 }
142 }
143 };
144
146 std::unordered_map<std::string, std::unique_ptr<UserDataPrinterBase>> user_data_printers_;
147
152 uint32_t required_ = 0;
153
154 public:
155
159 Node() = delete;
160
161 Node(Node* parent, const std::string& name,
162 const std::string& value, const std::string& origin) :
163 parent_(parent),
164 name_(name),
165 value_(value),
166 origin_(origin),
167 has_value_(true),
168 write_count_(1)
169 {;}
170
174 Node(Node* parent, const std::string& name) :
175 parent_(parent),
176 name_(name),
177 value_(),
178 has_value_(false),
179 write_count_(0)
180 {;}
181
187 Node(Node* parent, ParameterTree* tree) :
188 parent_(parent),
189 tree_(tree),
190 name_(""),
191 value_(),
192 has_value_(false),
193 write_count_(0)
194 {;}
195
200 Node(Node* parent, const Node& n) :
201 parent_(parent),
202 name_(n.name_),
203 value_(n.value_),
204 origin_(n.origin_),
205 has_value_(n.has_value_),
206 write_count_(n.write_count_),
207 read_count_(n.read_count_)
208 {
209 for(auto& x : n.children_){
210 children_.emplace_back(new Node(this, *x.get()));
211 }
212 }
213
220 Node& operator= (const Node& n) {
221 // preserve parent_
222 name_ = n.name_;
223 value_ = n.value_;
224 origin_ = n.origin_;
225 has_value_ = n.has_value_;
226 write_count_ = static_cast<decltype(write_count_)>(has_value_);
227 read_count_ = 0;
228 for(auto& x : n.children_){
229 children_.emplace_back(new Node(this, *x.get()));
230 }
231 return *this;
232 }
233
239 void dump(std::ostream& o) const {
240 o << "<VPT Node: \"" << name_ << "\" children:" << children_.size()
241 << " writes:" << write_count_ << " reads:" << read_count_ << " required:" << required_ << ">";
242 }
243
247 const std::string& getName() const { return name_; }
248
252 Node* getParent() { return parent_; }
253
257 Node const * getParent() const { return parent_; }
258
263 Node* p = this;
264 while(p->getParent() != nullptr){
265 p = p->getParent();
266 }
267 sparta_assert(p->getName().size() == 0);
268 return p;
269 }
270
274 Node const * getRoot() const {
275 Node const * p = this;
276 while(p->getParent() != nullptr){
277 p = p->getParent();
278 }
279 sparta_assert(p->getName().size() == 0);
280 return p;
281 }
282
287 return getRoot()->tree_;
288 }
289
293 const ParameterTree* getOwner() const {
294 return getRoot()->tree_;
295 }
296
300 std::string getPath() const {
301 std::stack<const std::string*> names;
302 Node const * n = this;
303 while(n && n->getName().size() > 0){ // Stop at null parent, or root (which has no name)
304 names.push(&n->getName());
305 n = n->getParent();
306 }
307
308 std::stringstream ss;
309 if(names.size() > 0){
310 ss << *names.top();
311 names.pop();
312 while(names.size() > 0){
313 ss << '.' << *names.top();
314 names.pop();
315 }
316 }
317 return ss.str();
318 }
319
323 bool isRoot() const {
324 return name_.size() == 0;
325 }
326
330 void incrementReadCount() const {
331 ++read_count_;
332 }
333
337 void resetReadCount() const {
338 read_count_ = 0;
339 }
340
345 uint32_t getReadCount() const {
346 return read_count_;
347 }
348
354 const std::string& getValue() const {
355 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
357 return value_;
358 }
359
365 const std::string& peekValue() const {
366 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
367 return value_;
368 }
369
374 const std::string& getOrigin() const {
375 sparta_assert(hasValue(), "Node \"" << name_ << "\" does not have a value associated with it");
376 return origin_;
377 }
378
391 template <typename T, typename=typename std::enable_if<!std::is_convertible<T, std::string>::value>::type>
392 T getAs() const {
393 return lexicalCast<T>(getValue());
394 }
395
399 template <typename T, typename=typename std::enable_if<std::is_convertible<T, std::string>::value>::type>
400 const std::string& getAs() const {
401 return getValue();
402 }
403
407 operator std::string () const {
408 return value_;
409 }
410
415 template <typename T>
416 operator T () const {
417 return getAs<T>();
418 }
419
424 template <typename T>
425 bool operator==(const T& rhp) const {
426 return getAs<T>() == rhp;
427 }
428
438 static bool matches(const std::string& pattern, const std::string& other) {
439 std::regex expr(TreeNode::createSearchRegexPattern(pattern));
440 std::smatch what;
441 return std::regex_match(other, what, expr);
442 }
443
451 Node* getChild(const std::string& name) const {
453 throw SpartaException("Cannot call ParameterTree::Node::getChild with a name that is a search pattern: \"")
454 << name << "\". addChild must be used instead";
455 }
456
457 // Always search in reverse-applied order to match on most recent changes first
458 auto itr = children_.rbegin();
459 for(; itr != children_.rend(); ++itr){
460 if(matches((*itr)->getName(), name)){
461 return itr->get();
462 }
463 }
464
465 return nullptr;
466 }
467
478 Node* create(const std::string& path, bool required) {
479 if(path.size() == 0){
480 return this;
481 }
482
483 size_t name_pos = 0;
484 std::string immediate_child_name;
485 immediate_child_name = TreeNode::getNextName(path, name_pos);
486 if(immediate_child_name.size() == 0){
487 // Cannot get parent like this
488 return nullptr; // TEMPORARY behavior. See docstring
489 //throw SpartaException("Virtual parameter path \"") << path
490 // << "\" is invalid because it contains an empty name (between two '.' "
491 // "characters). Parents cannot currently be referenced in the virtual parameter tree";
492 }
493
494 // Get a child of node if one exists with an exact match before any nodes
495 // with wilcards are encountered. If this node name includes a wildcard, a
496 // child will be returned IFF it the pattern string itself matches with the
497 // highest priority child in this node.
498 Node* child = getPriorityChildMatch(immediate_child_name);
499 if(!child){
500 child = addChild(immediate_child_name, required);
501 }
502
503 if(name_pos == std::string::npos){
504 return child;
505 }
506
507 const std::string remainder = path.substr(name_pos);
508 return child->create(remainder, required);
509 }
510
519 Node* getPriorityChildMatch(const std::string& name) const {
520 if(children_.size() == 0){
521 return nullptr;
522 }
523
524 // Becase the name is a pattern, it is impossible to math against other existing
525 // children which are patterns. Therefore, if a pattern child is encountered
526 // which does not exactly match the given name string, nullptr must be returned so
527 // that the caller appends a new node, whih will have the highest priority
528 const bool name_has_wildcard = TreeNode::hasWildcardCharacters(name);
529
530 // Always search in reverse-applied order to match on most recent changes first.
531 //
532 // This is an optimization to prevent nodes from being created needlessly. It tries
533 // to find a matching node while considering wildcards to avoid creating new nodes
534 // in most cases where it is possible.
535 //
536 // Read the rules below to understand what happens. In general, if we don't get an
537 // exact string match with an existing node before hitting another node which is
538 // an exclusive superset or incomplete subset of the 'name' argument's pattern (it
539 // may have no wildcards) then a new node will need to be added at the end of the
540 // children list to specify a value. An existing node cannot be overridden.
541 auto itr = children_.rbegin();
542 for(; itr != children_.rend(); ++itr){
543 if((*itr)->getName() == name){
544 // Found a node with an exact name match (no pattern matching) before
545 // hitting a pattern node. This node can be used to apply a new parameter
546 return itr->get();
547 }
548
549 if(name_has_wildcard){
550 if(TreeNode::hasWildcardCharacters((*itr)->getName())){
551 // Encountered a wildcard node whch was not an exact string match.
552 // No way to tell if the name pattern exactly matches this node's, so
553 // assume it doesn't
554 return nullptr;
555 }else if(matches(name, (*itr)->getName())){
556 // Node has not wildcards but matches the pattern in 'name'. Therfore,
557 // A new parameter with this name would affect this node and more. A new
558 // node will have to be added to override it.
559 return nullptr;
560 }else{
561 // Node has no wildcards and the name agument is a pattern which
562 // does not match it. Therefore, it can be ignored because this 'name'
563 // will not apply
564 continue;
565 }
566 }else if(matches((*itr)->getName(), name)){
567 // Encountered a wildcard node which matches on this name before hitting an
568 // exact match. Therefore, a new node must be created by the caller so that
569 // the parameter being set will affect a subset of this node's pattern
570 return nullptr;
571 }
572 }
573 // No matches or important mismatches
574 return nullptr;
575 }
576
580 Node* addChild(const std::string& name, bool required) {
581 sparta_assert(hasValue() == false,
582 "Cannot add a child to a virtual parameter tree node \"" << name_
583 << "\" since it already has a value: \"" << value_ << "\"");
584
585 children_.emplace_back(new Node(this, name));
586 if(required){
587 children_.back()->incRequired();
588 }
589 return (children_.back().get());
590 }
591
599 Node& operator[] (const std::string& name) const {
600 Node* child = getChild(name);
601 sparta_assert(child != nullptr,
602 "Node \"" << name_ << "\" has no child named \"" << name << "\"");
603 return *child;
604 }
605
606 Node& operator[] (const char* name) const {
607 return this->operator[](std::string(name));
608 }
609
617 bool hasValue() const {
618 return has_value_;
619 }
620
628 void setValue(const std::string& val, bool required=true, const std::string& origin="") {
629 //sparta_assert(parent_,
630 // "Cannot assign a value to the root node of a virtual parameter tree");
631 //sparta_assert(read_count_ == 0,
632 // "Cannot set(\"" << val << "\") on node \"" << name_ << "\" because it has been read already");
633 value_ = val;
634 origin_ = origin;
635 has_value_ = true;
636 write_count_++;
637 if(required){
638 required_ += 1;
639 }
640 }
641
645 void incRequired() {
646 required_ += 1;
647 }
648
653 void unrequire() {
654 required_ = 0;
655 for(auto & n : children_){
656 n->unrequire();
657 }
658 }
659
665 std::unique_ptr<Node> release() {
666 return parent_->release_(this);
667 }
668
682 bool set(const std::string& path, const std::string& val, bool required, const std::string& origin="") {
683 //sparta_assert(read_count_ == 0,
684 // "Cannot set(\"" << val << "\") on node \"" << name_
685 // << "\" because it has been read already");
686
687 // Set through root of the tree.
688 std::string full_path = getPath();
689 if(full_path.size() > 0 && path.size() > 0){
690 full_path += ".";
691 }
692 full_path += path;
693 return getOwner()->set(full_path, val, required, origin);
694 }
695
702 const std::string& operator= (const std::string& val) {
703 set("", val, true);
704 return val;
705 }
706
712 bool isRequired() const {
713 // Start at beginning. Another, later-written node may override
714 // this node if it has the same patch or a matching pattern.
715 if (getOwner()->isRequired(getPath()))
716 {
717 return true;
718 }
719 return false;
720 }
721
726 uint32_t getRequiredCount() const {
727 return required_;
728 }
729
733 std::vector<Node*> getChildren() {
734 std::vector<Node*> children;
735 for(auto & n : children_){
736 children.push_back(n.get());
737 }
738 return children;
739 }
740
744 std::vector<const Node*> getChildren() const {
745 std::vector<const Node*> children;
746 for(auto & n : children_){
747 children.push_back(n.get());
748 }
749 return children;
750 }
751
756 const std::string & name,
757 std::vector<Node*> & matching_nodes)
758 {
759 if (getName() == name) {
760 matching_nodes.emplace_back(this);
761 return;
762 }
763
764 const auto children = getChildren();
765 for (ParameterTree::Node * child : children) {
766 child->recursFindPTreeNodesNamed(name, matching_nodes);
767 }
768 }
769
774 const std::string & name,
775 std::vector<const Node*> & matching_nodes) const
776 {
777 if (getName() == name) {
778 matching_nodes.emplace_back(this);
779 return;
780 }
781
782 const auto children = getChildren();
783 for (const ParameterTree::Node * child : children) {
784 child->recursFindPTreeNodesNamed(name, matching_nodes);
785 }
786 }
787
795 template <typename Callback>
796 size_t recurseVisitNodes(Callback callback) {
797 size_t count = 0;
798 recurseVisitNodes_(callback, count);
799 return count;
800 }
801
809 template <typename Callback>
810 size_t recurseVisitNodes(Callback callback) const {
811 size_t count = 0;
812 recurseVisitNodes_(callback, count);
813 return count;
814 }
815
823 template <typename Callback>
824 size_t recurseVisitLeaves(Callback callback) {
825 size_t count = 0;
826 recurseVisitLeaves_(callback, count);
827 return count;
828 }
829
837 template <typename Callback>
838 size_t recurseVisitLeaves(Callback callback) const {
839 size_t count = 0;
840 recurseVisitLeaves_(callback, count);
841 return count;
842 }
843
847 void recursePrint(std::ostream& o, uint32_t indent=0, bool print_user_data=true) const {
848 for(uint32_t i=0; i<indent; ++i){
849 o << " ";
850 }
851 o << name_;
852 if(has_value_){
853 o << " = \"" << value_ << "\" (read " << read_count_ << ", written " << write_count_
854 << ", required " << required_ << ", origin '" << getOrigin() << "')";
855 }
856 o << "\n";
857 if(print_user_data){
858 printUserData(o, indent+2);
859 }
860 for(auto & n : children_){
861 n->recursePrint(o, indent+2, print_user_data);
862 }
863 }
864
868 void printUserData(std::ostream& o, uint32_t indent=0) const {
869 if (user_data_.empty()) {
870 return;
871 }
872
873 for(uint32_t i=0; i<indent; ++i){
874 o << " ";
875 }
876
877 o << "User data (" << getPath() << "):\n";
878 for (const auto & [ud_name, ud_printer] : user_data_printers_) {
879 std::any ud = user_data_.at(ud_name);
880 ud_printer->print(ud_name, ud, o, indent+2);
881 }
882 }
883
896 void appendTree(const Node* ot) {
897 sparta_assert(ot, "Cannot append a null virtual parameter tree");
898 if(ot->getName().size() > 0){
899 // Attach 'ot' node argument as child of this
900 const bool required = false; // The starting node is not required to exist.. only its node children
901 Node* child = create(ot->getName(), required);
902 child->recursAppendTree_(ot);
903 }else{
904 // 'ot' is a root node (no name). Merge it with this.
905 recursAppendTree_(ot);
906 }
907 }
908
913 template <typename T>
914 void setUserData(const std::string & name, const T & user_data) {
915 static_assert(std::is_copy_constructible<T>::value, "std::any only works with copyable types");
916 user_data_[name] = user_data;
917 user_data_printers_[name] = std::make_unique<UserDataPrinter<T>>();
918 }
919
924 template <typename T>
925 void setUserData(const std::string & name, T && user_data) {
926 static_assert(std::is_copy_constructible<T>::value, "std::any only works with copyable types");
927 user_data_[name] = std::move(user_data);
928 user_data_printers_[name] = std::make_unique<UserDataPrinter<T>>();
929 }
930
934 template <typename T>
935 const T & getUserData(const std::string & name) const {
936 constexpr bool must_exist = true;
937 return *tryGetUserData<T>(name, must_exist);
938 }
939
943 template <typename T>
944 T & getUserData(const std::string & name) {
945 constexpr bool must_exist = true;
946 return *tryGetUserData<T>(name, must_exist);
947 }
948
952 template <typename T>
953 const T * tryGetUserData(const std::string & name, bool must_exist = false) const {
954 auto it = user_data_.find(name);
955 if (it == user_data_.end()) {
956 if (must_exist) {
957 throw SpartaException("User data '") << name << "' does not exist for node '"
958 << getPath() << "'";
959 }
960 return nullptr;
961 }
962 return &std::any_cast<const T&>(it->second);
963 }
964
968 template <typename T>
969 T * tryGetUserData(const std::string & name, bool must_exist = false) {
970 auto it = user_data_.find(name);
971 if (it == user_data_.end()) {
972 if (must_exist) {
973 throw SpartaException("User data '") << name << "' does not exist for node '"
974 << getPath() << "'";
975 }
976 return nullptr;
977 }
978 return &std::any_cast<T&>(it->second);
979 }
980
985 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>> & map) const
986 {
987 for (const auto & key : getUserDataKeys()) {
988 if (auto ext = tryGetUserData<std::shared_ptr<TreeNode::ExtensionsBase>>(key)) {
989 map[this][key] = ext->get();
990 }
991 }
992
993 for (auto child : getChildren()) {
994 child->recurseGetAllNodeExtensions(map);
995 }
996 }
997
1001 std::set<std::string> getUserDataKeys() const {
1002 std::set<std::string> keys;
1003 for (const auto & [key, _] : user_data_) {
1004 keys.insert(key);
1005 }
1006 return keys;
1007 }
1008
1012 bool clearUserData(const std::string & name) {
1013 user_data_printers_.erase(name);
1014 if (user_data_.count(name)) {
1015 user_data_.erase(name);
1016 return true;
1017 }
1018 return false;
1019 }
1020
1024 size_t clearUserData() {
1025 user_data_printers_.clear();
1026 auto sz = user_data_.size();
1027 user_data_.clear();
1028 return sz;
1029 }
1030
1036 ChildVector::const_reverse_iterator itr_;
1037 public:
1038 MatchIterator(ChildVector::const_reverse_iterator itr) :
1039 itr_(itr)
1040 {;}
1041
1042 MatchIterator(const MatchIterator&) = default;
1043
1044 MatchIterator& operator=(const MatchIterator&) = default;
1045
1046 bool operator==(const MatchIterator& rhp) const {
1047 return itr_ == rhp.itr_;
1048 }
1049
1050 bool operator!=(const MatchIterator& rhp) const {
1051 return itr_ != rhp.itr_;
1052 }
1053
1054 void operator++(int) {++itr_;}
1055
1056 void operator++() {++itr_;}
1057
1058 bool matches(const std::string& other) const {
1059 return ParameterTree::Node::matches((*itr_)->getName(), other);
1060 }
1061
1062 const Node* get() const {
1063 return itr_->get();
1064 }
1065
1066 Node* get() {
1067 return itr_->get();
1068 }
1069
1070 const Node* operator->() const {
1071 return itr_->get();
1072 }
1073 };
1074
1075 friend class MatchIterator;
1076
1080 MatchIterator getMatcherBegin() const { return MatchIterator(children_.rbegin()); }
1081
1085 MatchIterator getMatcherEnd() const { return MatchIterator(children_.rend()); }
1086
1087 private:
1088
1093 void recursAppendTree_(const Node* ot) {
1094 // Inherit value. Never invalidate
1095 if(ot->hasValue()){
1096 setValue(ot->peekValue(), ot->getRequiredCount() > 0, ot->getOrigin());
1097 }
1098
1099 // Inherit user data.
1100 for(const auto & [ud_name, ud_value] : ot->user_data_){
1101 user_data_[ud_name] = ud_value;
1102 }
1103
1104 for(auto & child : ot->getChildren()){
1105 // TODO: copy required count instead of just boolean
1106 Node* c = create(child->getName(), child->getRequiredCount() > 0); // Create if needed
1107 c->recursAppendTree_(child);
1108 }
1109 }
1110
1111 template <typename Callback>
1112 bool recurseVisitNodes_(Callback callback, size_t & count) {
1113 ++count;
1114 auto keep_going = callback(this);
1115 if (!keep_going) {
1116 return false;
1117 }
1118 for (auto & child : children_) {
1119 if (!child->recurseVisitNodes_(callback, count)) {
1120 return false;
1121 }
1122 }
1123 return true;
1124 }
1125
1126 template <typename Callback>
1127 bool recurseVisitNodes_(Callback callback, size_t & count) const {
1128 ++count;
1129 auto keep_going = callback(this);
1130 if (!keep_going) {
1131 return false;
1132 }
1133 for (const auto & child : children_) {
1134 if (!child->recurseVisitNodes_(callback, count)) {
1135 return false;
1136 }
1137 }
1138 return true;
1139 }
1140
1141 template <typename Callback>
1142 bool recurseVisitLeaves_(Callback callback, size_t & count) {
1143 if (children_.empty()) {
1144 ++count;
1145 auto keep_going = callback(this);
1146 if (!keep_going) {
1147 return false;
1148 }
1149 }
1150
1151 for (auto & child : children_) {
1152 if (!child->recurseVisitLeaves_(callback, count)) {
1153 return false;
1154 }
1155 }
1156 return true;
1157 }
1158
1159 template <typename Callback>
1160 bool recurseVisitLeaves_(Callback callback, size_t & count) const {
1161 if (children_.empty()) {
1162 ++count;
1163 auto keep_going = callback(this);
1164 if (!keep_going) {
1165 return false;
1166 }
1167 }
1168
1169 for (const auto & child : children_) {
1170 if (!child->recurseVisitLeaves_(callback, count)) {
1171 return false;
1172 }
1173 }
1174 return true;
1175 }
1176
1177 std::unique_ptr<Node> release_(Node *node) {
1178 std::unique_ptr<Node> rtn;
1179 auto it = std::find_if(children_.begin(), children_.end(),
1180 [node] (const auto & child) -> bool {
1181 return (child.get() == node);
1182 });
1183 if (it != children_.end()) {
1184 rtn = std::move(*it);
1185 children_.erase(it);
1186 }
1187 return rtn;
1188 }
1189 };
1190
1195 root_(new Node(nullptr, this))
1196 {;}
1197
1198 ParameterTree(const ParameterTree& rhp) :
1199 root_(new Node(nullptr, this))
1200 {
1201 root_->appendTree(rhp.getRoot());
1202 }
1203
1204 ParameterTree& operator=(const ParameterTree& rhp) {
1205 clear();
1206 merge(rhp);
1207 return *this;
1208 }
1209
1210 //bool operator==(const ParameterTree& rhp) const {
1211 //}
1212
1216 virtual ~ParameterTree() {}
1217
1221 void clear() {
1222 root_.reset(new Node(nullptr, this)); // Clear all children
1223 }
1224
1239 bool set(const std::string& path, const std::string& value, bool required, const std::string& origin="") {
1240 Node* n = create(path, false); // inc required after setting value
1241 if(!n){
1242 return false;
1243 }
1244 n->setValue(value, required, origin);
1245
1246 return true;
1247 }
1248
1263 Node* create(const std::string& path, bool required=false) {
1264 if(path.size() == 0){
1265 return getRoot();
1266 }
1267
1268 return getRoot()->create(path, required);
1269 }
1270
1282 const Node& get(const std::string& path) const {
1283 const Node* node = tryGet(path);
1284 if(!node){
1285 throw SpartaException("Unable to find parameter in tree: \"") << path << "\"";
1286 }
1287 return *node;
1288 }
1289
1294 const Node& operator[] (const std::string& name) const {
1295 return get(name);
1296 }
1297
1304 bool hasValue(const std::string& path, const bool must_be_leaf = true) const {
1305 const Node* n = tryGet_(path, must_be_leaf);
1306 return n != nullptr && n->hasValue();
1307 }
1308
1315 bool exists(const std::string& path, const bool must_be_leaf = true) const {
1316 return tryGet_(path, must_be_leaf) != nullptr;
1317 }
1318
1330 uint32_t getUnreadValueNodes(std::vector<const Node*>* nodes) const {
1331 return recursCountUnreadValueNodes_<const Node>(root_.get(), nodes);
1332 }
1333
1345 uint32_t getUnreadValueNodes(std::vector<Node*>* nodes) {
1346 return recursCountUnreadValueNodes_<Node>(root_.get(), nodes);
1347 }
1348
1354 const Node* tryGet(const std::string& path, const bool must_be_leaf = true) const {
1355 return tryGet_(path, must_be_leaf);
1356 }
1357
1363 Node* tryGet(const std::string& path, const bool must_be_leaf = true) {
1364 return tryGet_(path, must_be_leaf);
1365 }
1366
1372 bool isRequired(const std::string& path) const {
1373
1374 if(path.size() == 0){
1375 return root_->getRequiredCount() > 0;
1376 }
1377
1378 std::string immediate_child_name;
1379 size_t name_pos = 0;
1380 immediate_child_name = TreeNode::getNextName(path, name_pos);
1381
1382 if(immediate_child_name.size() == 0){
1383 // Cannot get parent.
1384 throw SpartaException("Parameter ") << path
1385 << " is invalid because it contains an empty name (between two '.' "
1386 "characters). Parents cannot currently be refrenced in the parameter tree";
1387 }
1388
1389 bool match_found = false;
1390 const bool required = recursGetIsRequired_(root_.get(), path, immediate_child_name, name_pos, match_found);
1391 sparta_assert(match_found,
1392 "Asked ParameterTree if path \"" << path << "\" is required but no "
1393 "matching node was found in the ParameterTree");
1394 return required;
1395 }
1396
1402 bool unrequire(const std::string &path) {
1403 Node * node = tryGet_(path, false);
1404 if(nullptr != node) {
1405 node->unrequire();
1406 return true;
1407 }
1408 return false;
1409 }
1410
1419 bool isRead(const std::string& path) const {
1420 if(path.size() == 0){
1421 return root_->hasValue() && root_->getReadCount() > 0;
1422 }
1423
1424 std::string immediate_child_name;
1425 size_t name_pos = 0;
1426 immediate_child_name = TreeNode::getNextName(path, name_pos);
1427
1428 if(immediate_child_name.size() == 0){
1429 // Cannot get parent.
1430 throw SpartaException("Parameter ") << path
1431 << " is invalid because it contains an empty name (between two '.' "
1432 "characters). Parents cannot currently be refrenced in the parameter tree";
1433 }
1434
1435 return recursIsRead_(root_.get(), path, immediate_child_name, name_pos);
1436 }
1437
1438 Node const * getRoot() const { return root_.get(); }
1439
1440 Node * getRoot() { return root_.get(); }
1441
1449 void merge(const ParameterTree& rhp) {
1450 root_->appendTree(rhp.getRoot());
1451 }
1452
1460 template <typename Callback>
1461 size_t visitNodes(Callback callback) {
1462 return root_->recurseVisitNodes(callback);
1463 }
1464
1472 template <typename Callback>
1473 size_t visitNodes(Callback callback) const {
1474 return root_->recurseVisitNodes(callback);
1475 }
1476
1484 template <typename Callback>
1485 size_t visitLeaves(Callback callback) {
1486 return root_->recurseVisitLeaves(callback);
1487 }
1488
1496 template <typename Callback>
1497 size_t visitLeaves(Callback callback) const {
1498 return root_->recurseVisitLeaves(callback);
1499 }
1500
1504 void recursePrint(std::ostream& o, bool print_user_data=true) const {
1505 root_->recursePrint(o, 0, print_user_data); // Begin with 0 indent
1506 }
1507
1511 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>>
1513 {
1514 std::map<const Node*, std::map<std::string, const TreeNode::ExtensionsBase*>> all_ext_map;
1515 root_->recurseGetAllNodeExtensions(all_ext_map);
1516 return all_ext_map;
1517 }
1518
1519 private:
1520
1525 const Node* tryGet_(const std::string& path, const bool must_be_leaf = true) const {
1526 if(path.size() == 0){
1527 return root_.get();
1528 }
1529
1530 std::string immediate_child_name;
1531 size_t name_pos = 0;
1532 immediate_child_name = TreeNode::getNextName(path, name_pos);
1533
1534 if(immediate_child_name.size() == 0){
1535 // Cannot get parent.
1536 throw SpartaException("Parameter ") << path
1537 << " is invalid because it contains an empty name (between two '.' "
1538 "characters). Parents cannot currently be refrenced in the parameter tree";
1539 }
1540
1541 return recursTryGet_<const Node>(static_cast<const Node*>(root_.get()), path,
1542 immediate_child_name, name_pos, must_be_leaf);
1543 }
1544
1549 Node* tryGet_(const std::string& path, const bool must_be_leaf = true) {
1550 if(path.size() == 0){
1551 return root_.get();
1552 }
1553
1554 std::string immediate_child_name;
1555 size_t name_pos = 0;
1556 immediate_child_name = TreeNode::getNextName(path, name_pos);
1557
1558 if(immediate_child_name.size() == 0){
1559 // Cannot get parent.
1560 throw SpartaException("Parameter ") << path
1561 << " is invalid because it contains an empty name (between two '.' "
1562 "characters). Parents cannot currently be refrenced in the parameter tree";
1563 }
1564
1565 return recursTryGet_(root_.get(), path,
1566 immediate_child_name, name_pos, must_be_leaf);
1567 }
1568
1569 template<typename NodeT>
1570 static NodeT* recursTryGet_(NodeT* node, const std::string& path,
1571 const std::string& match_name,
1572 size_t name_pos, const bool must_be_leaf)
1573 {
1575 "Cannot attempt to read a node with a path containing wildcard "
1576 "characters. A specific node path must be used. Error in \""
1577 << match_name << "\" from \"" << path << "\"");
1578
1579 if(name_pos == std::string::npos){
1580 // End of the search.. No deeper
1581 NodeT* result = nullptr;
1582 NodeT* backup = nullptr; // First match (if is has no value)
1583 auto itr = node->getMatcherBegin();
1584 for(; itr != node->getMatcherEnd(); ++itr){
1585 if(itr.matches(match_name)){
1586 if(itr.get()->hasValue() || !must_be_leaf){
1587 itr.get()->incrementReadCount();
1588 if(!result){
1589 result = itr.get(); // Found it
1590 }
1591 }else if(!backup){
1592 backup = itr.get();
1593 }
1594 }
1595 }
1596 if(result){
1597 return result;
1598 }
1599 return backup; // No match here
1600 }
1601
1602 // Search deeper
1603 std::string immediate_child_name;
1604 immediate_child_name = TreeNode::getNextName(path, name_pos);
1605
1606 if(immediate_child_name.size() == 0){
1607 // Cannot get parent.
1608 throw SpartaException("Parameter ") << path
1609 << " is invalid because it contains an empty name (between two '.' "
1610 "characters). Parents cannot currently be refrenced in the parameter tree";
1611 }
1612
1613 NodeT* result = nullptr;
1614 auto itr = node->getMatcherBegin();
1615 for(; itr != node->getMatcherEnd(); ++itr){
1616 if(itr.matches(match_name)){
1617 NodeT* match = recursTryGet_(itr.get(), path, immediate_child_name, name_pos, must_be_leaf);
1618 if(match && (match->hasValue() || !must_be_leaf)) {
1619 match->incrementReadCount();
1620 if(result == nullptr){
1621 result = match;
1622 }
1623 }
1624 // Keep this match as result and continue iterating to mark all matching nodes as read
1625 }
1626 }
1627 return result;
1628 }
1629
1633 bool recursGetIsRequired_(const Node* node,
1634 const std::string& path,
1635 const std::string& match_name,
1636 size_t name_pos,
1637 bool& found_match) const
1638 {
1639 found_match = false;
1640
1641 if(name_pos == std::string::npos){
1642 // End of the search.. No deeper
1643 auto itr = node->getMatcherBegin();
1644 for(; itr != node->getMatcherEnd(); ++itr){
1645 if(TreeNode::hasWildcardCharacters(match_name)
1646 ? itr->getName() == match_name // Exact name match if match_name has wildcards
1647 : itr.matches(match_name)) // Pattern match if match_name has no wildcards
1648 {
1649 found_match = true;
1650 return itr.get()->getRequiredCount() > 0;
1651 }
1652 }
1653
1654 return false; // dummy
1655 }
1656
1657 // Search deeper
1658 std::string immediate_child_name;
1659 immediate_child_name = TreeNode::getNextName(path, name_pos);
1660
1661 if(immediate_child_name.size() == 0){
1662 // Cannot get parent.
1663 throw SpartaException("Parameter ") << path
1664 << " is invalid because it contains an empty name (between two '.' "
1665 "characters). Parents cannot currently be refrenced in the parameter tree";
1666 }
1667
1668 auto itr = node->getMatcherBegin();
1669 for(; itr != node->getMatcherEnd(); ++itr){
1670 if(itr.matches(match_name)){
1671 const bool required = recursGetIsRequired_(itr.get(), path, immediate_child_name, name_pos, found_match);
1672 if(found_match){
1673 return required;
1674 }
1675 // No match found. Keep going
1676 }
1677 }
1678
1679 sparta_assert(found_match == false); // Should not have been set
1680 return false; // dummy
1681 }
1682
1686 template<class NodeT>
1687 static uint32_t recursCountUnreadValueNodes_(NodeT* n, std::vector<NodeT*> * nodes) {
1688 uint32_t count = 0;
1689 if(n->hasValue() && (n->getReadCount() == 0)){
1690 count = 1;
1691 if(nodes){
1692 nodes->push_back(n);
1693 }
1694 }
1695 auto itr = n->getMatcherBegin();
1696 for(; itr != n->getMatcherEnd(); ++itr){
1697 count += recursCountUnreadValueNodes_<NodeT>(itr.get(), nodes);
1698 }
1699 return count;
1700 }
1701
1705 bool recursIsRead_(const Node* node,
1706 const std::string& path,
1707 const std::string& match_name,
1708 size_t name_pos) const
1709 {
1711 "Cannot attempt to read a node with a path containing wildcard "
1712 "characters. A specific node path must be used. Error in \""
1713 << match_name << "\" from \"" << path << "\"");
1714
1715 if(name_pos == std::string::npos){
1716 auto itr = node->getMatcherBegin();
1717 for(; itr != node->getMatcherEnd(); ++itr){
1718 if(itr.matches(match_name)){
1719 if(itr.get()->hasValue() && itr.get()->getReadCount() > 0){
1720 return true;
1721 }
1722 }
1723 }
1724 return false;
1725 }
1726
1727 // Search deeper
1728 std::string immediate_child_name;
1729 immediate_child_name = TreeNode::getNextName(path, name_pos);
1730 if(immediate_child_name.size() == 0){
1731 // Cannot get parent.
1732 throw SpartaException("Parameter ") << path
1733 << " is invalid because it contains an empty name (between two '.' "
1734 "characters). Parents cannot currently be refrenced in the parameter tree";
1735 }
1736
1737 auto itr = node->getMatcherBegin();
1738 for(; itr != node->getMatcherEnd(); ++itr){
1739 if(itr.matches(match_name)){
1740 bool read = recursIsRead_(itr.get(), path, immediate_child_name, name_pos);
1741 if(read){
1742 return true;
1743 }
1744 }
1745 }
1746 return false;
1747 }
1748
1752 std::unique_ptr<Node> root_;
1753
1754 }; // class ParameterSet
1755
1756 inline std::ostream& operator<<(std::ostream& o, const ParameterTree::Node& n) {
1757 n.dump(o);
1758 return o;
1759 }
1760
1761 inline std::ostream& operator<<(std::ostream& o, const ParameterTree::Node* n) {
1762 if(!n){
1763 o << "<null ParameterTree::Node>";
1764 }else{
1765 o << *n;
1766 }
1767 return o;
1768 }
1769
1770} // 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.
size_t recurseVisitNodes(Callback callback) const
Apply the given callback to all nodes.
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.
size_t recurseVisitLeaves(Callback 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.
size_t recurseVisitLeaves(Callback callback)
Apply the given callback to all leaf nodes.
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.
size_t recurseVisitNodes(Callback callback)
Apply the given callback to all nodes.
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.
size_t visitNodes(Callback callback)
Apply the given callback to all nodes.
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.
size_t visitNodes(Callback callback) const
Apply the given callback to all nodes.
size_t visitLeaves(Callback callback) const
Apply the given callback to all leaf nodes.
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 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 ...
size_t visitLeaves(Callback callback)
Apply the given callback to all leaf nodes.
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