SST 16.1.0
Structural Simulation Toolkit
ObjTreeLeaves.h
1// Copyright 2009-2026 NTESS. Under the terms
2// of Contract DE-NA0003525 with NTESS, the U.S.
3// Government retains certain rights in this software.
4//
5// Copyright (c) 2009-2026, NTESS
6// All rights reserved.
7//
8// This file is part of the SST software package. For license
9// information, see the LICENSE file in the top level directory of the
10// distribution.
11
12#ifndef SST_CORE_SERIALIZATION_OBJECTTREELEAVES_DEBUGGER_H
13#define SST_CORE_SERIALIZATION_OBJECTTREELEAVES_DEBUGGER_H
14#include <cstddef>
15#include <cstdint>
16#include <functional>
17#include <iomanip>
18#include <ios>
19#include <iostream>
20#include <memory>
21#include <ostream>
22#include <string>
23#include <string_view>
24#include <utility>
25#include <variant>
26#include <vector>
27
28namespace SST {
30}
31
32namespace SST::Core::Serialization {
33/**
34 * The Object Tree (ObjTree) is intended to be a developer friendly interface
35 * to SST’s existing serialization infrastructure - ObjectMap. This is the
36 * base class container from which the type-specific nodes derive. All nodes
37 * stored in a std::vector of std::unique_ptr
38 */
39
40class ObjTreeCont
41{
42public:
43 enum class NodeKind { Generic, Integer, Float, String, Bool, Component, Container, FRef, GenericVal };
44
45protected:
46 ObjTreeCont* parent_ = nullptr;
47 std::vector<std::unique_ptr<ObjTreeCont>> children_;
48 std::string name_ = "uninit";
49 std::string type_ = "uninit";
50 NodeKind kind_ = NodeKind::Generic;
51 bool readOnly_ = false;
52
53public:
54
55 ObjTreeCont() = default;
56 ObjTreeCont(const std::string& name, const std::string& type, NodeKind kind = NodeKind::Generic) :
57 name_(name),
58 type_(type),
59 kind_(kind)
60 {}
61 virtual ~ObjTreeCont() = default;
62
63 ObjTreeCont(const ObjTreeCont& rhs) :
64 name_(rhs.name_),
65 type_(rhs.type_),
66 kind_(rhs.kind_),
67 readOnly_(rhs.readOnly_)
68 {
69 children_.reserve(rhs.children_.size());
70 for ( const auto& child : rhs.children_ ) {
71 auto* cloned = child->clone();
72 cloned->parent_ = this;
73 children_.emplace_back(cloned);
74 }
75 }
76
77 ObjTreeCont& operator=(const ObjTreeCont& rhs)
78 {
79 if ( this == &rhs ) return *this;
80 parent_ = nullptr;
81 name_ = rhs.name_;
82 type_ = rhs.type_;
83 kind_ = rhs.kind_;
84 children_.clear();
85 children_.reserve(rhs.children_.size());
86 for ( const auto& child : rhs.children_ ) {
87 auto* cloned = child->clone();
88 cloned->parent_ = this;
89 children_.emplace_back(cloned);
90 }
91 return *this;
92 }
93
94 virtual ObjTreeCont* clone() const { return new ObjTreeCont(*this); }
95
96 /**
97 Adds a child node to the current node. obj ownership managed by
98 std::unique_ptr. Sets the parent of obj to the current node (this)
99
100 @param obj node to insert
101
102 */
103 void addChildObj(ObjTreeCont* obj)
104 {
105 children_.push_back(std::unique_ptr<ObjTreeCont>(obj));
106 obj->parent_ = this;
107 }
108
109 void setParent(ObjTreeCont* p) { parent_ = p; }
110 ObjTreeCont* getParent() const { return parent_; }
111 const std::vector<std::unique_ptr<ObjTreeCont>>& getChildren() const { return children_; }
112
113 const std::string& getObjName() const { return name_; }
114 const std::string& getType() const { return type_; }
115 void setName(const std::string& name) { name_ = name; }
116 /**
117 * Set the type of the node - this comes from ObjectMap/Serialization. The type
118 * will be slightly demangled by the prettifyType() function in that
119 * std::__1:: and std::__cxx11:: are replaced with just std::
120
121 @param type - a string representing te C++ type of the node
122
123 */
124 void setType(const std::string& type) { type_ = prettifyType(type); }
125
126 /**
127 Returns true if this is the top of the tree - determined by the
128 parent pointer being nullptr
129
130
131 @return true if parent == null, false otherwise
132 */
133
134 bool isRoot() { return parent_ == nullptr; }
135
136 /**
137 * Function that will be called if during conversion from ObjectMap
138 * the node has been marked as ReadOnly. Once a node is marked ReadOnly
139 * it cannont be made writeable.
140 */
141 void makeReadOnly() { readOnly_ = true; }
142 bool isReadOnly() { return readOnly_; }
143
144 /**
145 Apply a function to all children of this node regardless of type, etc.
146 Depending on the nature of func, it may descend to the next level (children's
147 children) of the tree. An example being the Dump() in ContainerObj
148
149 @param func - callable function (function pointer, lambda, etc)
150
151 */
152 template <typename Func>
153 void applyRecursive(Func&& func)
154 {
155 for ( auto& child : children_ ) {
156 func(child.get());
157 }
158 }
159 /**
160 Apply a function to all children of this node of a specific type. Type
161 is determined through a successful dynamic_cast to ObjectType
162 Depending on the nature of func, it may descend to the next level (children's
163 children) of the tree. An example being the Dump() in ContainerObj
164
165 @param func - callable function (function pointer, lambda, etc)
166
167 */
168 template <typename ObjectType, typename Func>
169 void applyRecursiveByType(Func&& func)
170 {
171 for ( auto& child : children_ ) {
172 if ( auto* child_t = dynamic_cast<ObjectType*>(child.get()) ) {
173 func(child_t);
174 }
175 }
176 }
177
178 /**
179 Find the *first* instance of a particular type in this node's children_ that
180 matches criteria set by predicate. Examples:
181 // Find a ComponentObj whose BaseComponent has a specific name
182 auto* router = root->findByType<ComponentObj>([](ComponentObj* c) {
183 return c->getVal()->getName() == "router";
184 });
185
186 // Find the first IntegerObj with value greater than 100
187 auto* big = root->findByType<IntegerObj>([](IntegerObj* i) {
188 return i->visit([](auto val) { return static_cast<int64_t>(val) > 100; });
189 });
190
191 // Find any StringObj (predicate always returns true)
192 auto* anyStr = root->findByType<StringObj>([](StringObj*) {
193 return true;
194 });
195
196 // Find a ContainerObj that's a vector
197 auto* vec = root->findByType<ContainerObj>([](ContainerObj* c) {
198 return c->getContainerType().find("vector") != std::string::npos;
199 });
200 @param predicate - callable function (function ptr, lambda, etc)
201
202 @return pointer to matched node, nullptr if not found
203 */
204 template <typename ObjectType, typename Func>
205 ObjectType* findByType(Func&& predicate)
206 {
207 for ( auto& child : children_ ) {
208 if ( auto* child_t = dynamic_cast<ObjectType*>(child.get()) ) {
209 if ( predicate(child_t) ) return child_t;
210 }
211 }
212 return nullptr;
213 }
214
215 /**
216 * Find the *first* instance of a child with name_ matching name. Does
217 * not descend into the tree.
218
219 @param name - name of the node you are searching for
220
221 @return pointer to node, if found, nullptr otherwise
222 */
223 ObjTreeCont* findByName(const std::string name)
224 {
225 for ( size_t i = 0; i < children_.size(); i++ ) {
226 if ( children_[i]->getObjName() == name ) {
227 return children_[i].get();
228 }
229 }
230 return nullptr;
231 }
232
233 virtual void apply() {};
234 virtual std::string getTypeName() const { return type_; };
235
236 /**
237 * Print the contents of the node to the stream specified by os. Verbosity
238 * Level 0 will only print "/" - applicable to the root node only. Any higher
239 * Verbosity will trigger a call to Dump() to all children.
240
241 @param verbosity - Level 0: Print this node only, which for the base class is "/"
242 Level > 0: Call Dump on all children_
243 @param base - Control the base (dec, hex, oct) to print numerical values in. Default dec.
244 @param os - Stream to send the output. Default std::cout
245
246 @return pointer to node, if found, nullptr otherwise
247 */
248 virtual void Dump(
249 const int verbosity, std::ios_base::fmtflags base = std::ios_base::dec, std::ostream& os = std::cout)
250 {
251 os << name_ << "/ " << std::endl; //(" << type_ << ")" << std::endl;
252 if ( verbosity > 0 ) {
253 applyRecursive([&](ObjTreeCont* child) { child->Dump(verbosity - 1, base, os); });
254 }
255 }
256
257 /**
258 * Set the value stored in the node from a string. This function will be
259 * called when the user wants to update the live value in the SST simulation.
260 * The function will convert value into the correct type based on the tree node
261 * type.
262 * @param value - string representing the value you want to wrtie to the live sim
263 * @return - true if successful, false otherwise
264 */
265 virtual bool setFromString([[maybe_unused]] const std::string& value) { return false; }
266
267 /**
268 * This function will update the local stored node value with the live value
269 * in the sst simulation.
270 */
271 virtual void syncFromSim() {};
272
273 /**
274 * This function will compare the stored the node value with the live simulation
275 * value. This function will NOT modify the stored value - an independent call
276 * to syncFromSim() is required.
277 * @return true if the value in the live simulation differs from the stored
278 * node value. false otherwise
279 */
280 virtual bool hasChanged() { return false; }
281
282 NodeKind getKind() const { return kind_; }
283 bool isComponent() const { return kind_ == NodeKind::Component; }
284 bool isLeaf() const
285 {
286 return kind_ == NodeKind::Integer || kind_ == NodeKind::Float || kind_ == NodeKind::String ||
287 kind_ == NodeKind::Bool || kind_ == NodeKind::GenericVal;
288 }
289
290 /**
291 * Clears the entire Object Tree and frees all nodes
292 */
293 virtual void clear() { children_.clear(); }
294
295protected:
296
297 static void replaceAll(std::string& s, std::string_view from, std::string_view to)
298 {
299 if ( from.empty() ) return;
300 size_t pos = 0;
301 while ( (pos = s.find(from, pos)) != std::string::npos ) {
302 s.replace(pos, from.size(), to);
303 pos += to.size();
304 }
305 }
306
307 static std::string prettifyType(std::string t)
308 {
309 replaceAll(t, "std::__1::", "std::");
310 replaceAll(t, "std::__cxx11::", "std::");
311 replaceAll(t, " ", " ");
312 return t;
313 }
314};
315
316template <typename Obj_T>
317class ObjTree : public ObjTreeCont
318{
319public:
320 ObjTree() = default;
321 ObjTree(NodeKind kind) :
322 ObjTreeCont("", "", kind)
323 {}
324
325 ObjTree(const ObjTree<Obj_T>& rhs) :
326 ObjTreeCont(rhs),
327 objects_()
328 {
329 objects_.reserve(rhs.objects_.size());
330 for ( const auto& obj : rhs.objects_ ) {
331 auto* cloned = obj->clone();
332 cloned->setParent(this);
333 objects_.emplace_back(cloned);
334 }
335 }
336
337 ObjTree<Obj_T>& operator=(const ObjTree<Obj_T>& other)
338 {
339 if ( this == &other ) return *this;
340 ObjTreeCont::operator=(other);
341 objects_.clear();
342 objects_.reserve(other.objects_.size());
343 for ( const auto& obj : other.objects_ ) {
344 auto* cloned = obj->clone();
345 cloned->setParent(this);
346 objects_.emplace_back(cloned);
347 }
348 return *this;
349 }
350 /**
351 * Each time we break into the debugger we rebuild the ObjectTree based
352 * on the current ComponentInfoMap. A helper class is employed,
353 * ObjectMapToTree, which performs the conversion. The initial conversion
354 * of the component map will strictly be the top level components - the
355 * remainder of the tree is built dynamically through cd or through a call
356 * to print with a -r (recursive) option. When a user cd’s into a component
357 * for the first time all elements in that component *except* Sub Components are
358 * serialized and converted into ObjTree node types by the ObjectMapToTree
359 * class.
360
361 @param compMap - The current Component Info Map supplied by the SST simulation
362
363 @return none
364 */
365 void BuildTree(const ComponentInfoMap& compMap);
366
367 Obj_T& getObj() { return static_cast<Obj_T&>(*this); }
368 const Obj_T& getObj() const { return static_cast<const Obj_T&>(*this); }
369
370
371 template <typename ObjectType, typename Func>
372 void applyRecursiveByType(Func&& func)
373 {
374 for ( auto& child : children_ ) {
375 if ( auto* child_t = dynamic_cast<ObjectType*>(child.get()) ) {
376 func(child_t);
377 }
378 }
379 }
380
381 std::string getTypeName() const override { return typeid(Obj_T).name(); }
382
383 bool isEmpty() { return objects_.empty() && children_.empty(); }
384
385 void Dump([[maybe_unused]] const int verbosity, [[maybe_unused]] std::ios_base::fmtflags base = std::ios_base::dec,
386 std::ostream& os = std::cout) override
387 {
388 os << "Root/" << std::endl;
389 }
390
391 /**
392 * Clears the entire Object Tree and frees all nodes
393 */
394 void clear() override
395 {
397 objects_.clear();
398 }
399
400protected:
401 std::vector<std::unique_ptr<ObjTreeCont>> objects_;
402};
403
404class IntegerObj : public ObjTree<IntegerObj>
405{
406public:
407 using IntVariant = std::variant<int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t>;
408
409private:
410 IntVariant val_;
411 void* addr_ = nullptr;
412 std::function<int64_t()> getter_; // accessor-callback mode
413 std::function<void(int64_t)> setter_;
414 bool useAccessor_ = false;
415
416public:
417 template <typename T>
418 IntegerObj(T v, void* addr) :
419 ObjTree<IntegerObj>(NodeKind::Integer),
420 val_(v),
421 addr_(addr)
422 {}
423 IntegerObj(std::function<int64_t()> getter, std::function<void(int64_t)> setter) :
424 ObjTree<IntegerObj>(NodeKind::Integer),
425 val_(int64_t { 0 }),
426 getter_(std::move(getter)),
427 setter_(std::move(setter)),
428 useAccessor_(true)
429 {
430 if ( getter_ ) val_ = static_cast<int64_t>(getter_());
431 if ( !setter_ ) readOnly_ = true;
432 }
433
434 IntegerObj(const IntegerObj& rhs) :
435 ObjTree<IntegerObj>(rhs),
436 val_(rhs.val_),
437 addr_(rhs.addr_),
438 getter_(rhs.getter_),
439 setter_(rhs.setter_),
440 useAccessor_(rhs.useAccessor_)
441 {}
442 IntegerObj& operator=(const IntegerObj& rhs)
443 {
444 if ( this == &rhs ) return *this;
445 ObjTree<IntegerObj>::operator=(rhs);
446 val_ = rhs.val_;
447 addr_ = rhs.addr_;
448 getter_ = rhs.getter_;
449 setter_ = rhs.setter_;
450 useAccessor_ = rhs.useAccessor_;
451 return *this;
452 }
453 ObjTreeCont* clone() const override { return new IntegerObj(*this); }
454
455 template <typename T>
456 T getVal() const
457 {
458 return std::get<T>(val_);
459 }
460
461 template <typename T>
462 void setVal(T v)
463 {
464 val_ = v;
465 }
466
467 template <typename T>
468 void setSimVal(T v)
469 {
470 val_ = v;
471 syncToSim();
472 }
473
474 virtual void syncFromSim() override
475 {
476 if ( useAccessor_ && getter_ ) {
477 const int64_t live = getter_();
478 std::visit([live](auto& v) { v = static_cast<std::decay_t<decltype(v)>>(live); }, val_);
479 }
480 else if ( addr_ ) {
481 std::visit([this](auto& v) { v = *static_cast<std::decay_t<decltype(v)>*>(addr_); }, val_);
482 }
483 }
484
485 virtual bool hasChanged() override
486 {
487 if ( useAccessor_ && getter_ ) {
488 const int64_t live = getter_();
489 return std::visit([live](const auto& v) -> bool { return static_cast<int64_t>(v) != live; }, val_);
490 }
491 else if ( addr_ ) {
492 return std::visit(
493 [this](const auto& v) -> bool { return v != *static_cast<std::decay_t<decltype(v)>*>(addr_); }, val_);
494 }
495 return false;
496 }
497
498 template <typename Visitor>
499 auto visit(Visitor&& visitor)
500 {
501 return std::visit(std::forward<Visitor>(visitor), val_);
502 }
503
504 template <typename Visitor>
505 auto visit(Visitor&& visitor) const
506 {
507 return std::visit(std::forward<Visitor>(visitor), val_);
508 }
509
510 void apply() override
511 {
512 visit([](auto val) { std::cout << "Processing integer: " << static_cast<int64_t>(val) << std::endl; });
513 }
514
515 void Dump(
516 const int verbosity, std::ios_base::fmtflags base = std::ios_base::dec, std::ostream& os = std::cout) override
517 {
518 std::string ro = (readOnly_) ? " (ro)" : "";
519 if ( verbosity == 0 ) {
520 os << getObjName() << ro << std::endl;
521 }
522 else if ( verbosity == 1 ) {
523 visit([&](auto val) {
524 auto old_flags = os.flags();
525 os.setf(base, std::ios_base::basefield);
526 if ( base != std::ios_base::dec ) {
527 os.setf(std::ios_base::showbase);
528 }
529 os << getObjName() << ro << " = " << static_cast<int64_t>(val) << std::endl;
530 os.flags(old_flags);
531 });
532 }
533 else {
534 visit([&](auto val) {
535 auto old_flags = os.flags();
536 os.setf(base, std::ios_base::basefield);
537 if ( base != std::ios_base::dec ) {
538 os.setf(std::ios_base::showbase);
539 }
540 os << getObjName() << ro << " = " << static_cast<int64_t>(val) << " (" << getType() << ")" << std::endl;
541 os.flags(old_flags);
542 });
543 }
544 }
545 bool setFromString(const std::string& value) override
546 {
547 if ( readOnly_ ) {
548 std::cout << "WARNING Cannot set ReadOnly Obj " << name_ << std::endl;
549 return false;
550 }
551 try {
552 visit([&](auto& current) {
553 using T = std::decay_t<decltype(current)>;
554 setSimVal(static_cast<T>(std::stoll(value)));
555 });
556 return true;
557 }
558 catch ( ... ) {
559 return false;
560 }
561 }
562
563private:
564 void syncToSim()
565 {
566 if ( !addr_ ) return;
567 std::visit([this](auto& v) { *static_cast<std::decay_t<decltype(v)>*>(addr_) = v; }, val_);
568 }
569};
570
571class FloatObj : public ObjTree<FloatObj>
572{
573public:
574 using FloatVariant = std::variant<float, double, long double>;
575
576private:
577 FloatVariant val_;
578 void* addr_ = nullptr;
579 std::function<long double()> getter_; // accessor-callback mode
580 std::function<void(long double)> setter_;
581 bool useAccessor_ = false;
582
583public:
584 template <typename T>
585 FloatObj(T v, void* addr) :
586 ObjTree<FloatObj>(NodeKind::Float),
587 val_(v),
588 addr_(addr)
589 {}
590 FloatObj(std::function<long double()> getter, std::function<void(long double)> setter) :
591 ObjTree<FloatObj>(NodeKind::Integer),
592 val_(0.0),
593 getter_(std::move(getter)),
594 setter_(std::move(setter)),
595 useAccessor_(true)
596 {
597 if ( getter_ ) val_ = static_cast<long double>(getter_());
598 if ( !setter_ ) readOnly_ = true;
599 }
600
601 FloatObj(const FloatObj& rhs) :
602 ObjTree<FloatObj>(rhs),
603 val_(rhs.val_),
604 addr_(rhs.addr_),
605 getter_(rhs.getter_),
606 setter_(rhs.setter_),
607 useAccessor_(rhs.useAccessor_)
608 {}
609
610 FloatObj& operator=(const FloatObj& rhs)
611 {
612 if ( this == &rhs ) return *this;
613 ObjTree<FloatObj>::operator=(rhs);
614 val_ = rhs.val_;
615 addr_ = rhs.addr_;
616 getter_ = rhs.getter_;
617 setter_ = rhs.setter_;
618 useAccessor_ = rhs.useAccessor_;
619 return *this;
620 }
621
622 ObjTreeCont* clone() const override { return new FloatObj(*this); }
623
624 template <typename T>
625 T getVal() const
626 {
627 return std::get<T>(val_);
628 }
629
630 template <typename T>
631 void setVal(T v)
632 {
633 val_ = v;
634 }
635
636 template <typename T>
637 void setSimVal(T v)
638 {
639 val_ = v;
640 syncToSim();
641 }
642
643 virtual void syncFromSim() override
644 {
645 if ( useAccessor_ && getter_ ) {
646 const long double live = getter_();
647 std::visit([live](auto& v) { v = static_cast<std::decay_t<decltype(v)>>(live); }, val_);
648 }
649 else if ( addr_ ) {
650 std::visit([this](auto& v) { v = *static_cast<std::decay_t<decltype(v)>*>(addr_); }, val_);
651 }
652 }
653
654 virtual bool hasChanged() override
655 {
656 if ( useAccessor_ && getter_ ) {
657 const long double live = getter_();
658 return std::visit(
659 [live](const auto& v) -> bool { return static_cast<long double>(v) != static_cast<long double>(live); },
660 val_);
661 }
662 else if ( addr_ ) {
663 return std::visit(
664 [this](const auto& v) -> bool { return v != *static_cast<std::decay_t<decltype(v)>*>(addr_); }, val_);
665 }
666 return false;
667 }
668
669 template <typename Visitor>
670 auto visit(Visitor&& visitor)
671 {
672 return std::visit(std::forward<Visitor>(visitor), val_);
673 }
674
675 template <typename Visitor>
676 auto visit(Visitor&& visitor) const
677 {
678 return std::visit(std::forward<Visitor>(visitor), val_);
679 }
680
681 void apply() override
682 {
683 visit([](auto val) { std::cout << "Processing float: " << val << std::endl; });
684 }
685 void Dump(
686 const int verbosity, std::ios_base::fmtflags base = std::ios_base::dec, std::ostream& os = std::cout) override
687 {
688 std::string ro = (readOnly_) ? " (ro)" : "";
689 if ( verbosity == 0 ) {
690 os << getObjName() << ro << std::endl;
691 }
692 else if ( verbosity == 1 ) {
693 visit([&](auto val) {
694 auto old_flags = os.flags();
695 auto old_prec = os.precision();
696 os.setf(base, std::ios_base::basefield);
697 if ( base != std::ios_base::dec ) {
698 os.setf(std::ios_base::showbase);
699 }
700 os << getObjName() << ro << std::fixed << std::setprecision(12) << " = " << val << std::endl;
701 os.flags(old_flags);
702 os.precision(old_prec);
703 });
704 }
705 else {
706 visit([&](auto val) {
707 auto old_flags = os.flags();
708 auto old_prec = os.precision();
709 os.setf(base, std::ios_base::basefield);
710 if ( base != std::ios_base::dec ) {
711 os.setf(std::ios_base::showbase);
712 }
713 os << getObjName() << ro << std::fixed << std::setprecision(12) << " = " << val << " (" << getType()
714 << ")" << std::endl;
715 os.flags(old_flags);
716 os.precision(old_prec);
717 });
718 }
719 }
720
721 bool setFromString(const std::string& value) override
722 {
723 if ( readOnly_ ) {
724 std::cout << "WARNING Cannot set ReadOnly Obj " << name_ << std::endl;
725 return false;
726 }
727 try {
728 visit([&](auto& current) {
729 using T = std::decay_t<decltype(current)>;
730 setSimVal(static_cast<T>(std::stold(value)));
731 });
732 return true;
733 }
734 catch ( ... ) {
735 return false;
736 }
737 }
738
739private:
740 void syncToSim()
741 {
742 if ( !addr_ ) return;
743 std::visit([this](auto& v) { *static_cast<std::decay_t<decltype(v)>*>(addr_) = v; }, val_);
744 }
745};
746
747class ContainerObj : public ObjTree<ContainerObj>
748{
749 size_t size_ = 0;
750 ObjectMap* sourceMap_ = nullptr;
751
752public:
753 ContainerObj(const std::string& name, const std::string& type, size_t size, ObjectMap* source) :
754 ObjTree<ContainerObj>(NodeKind::Container),
755 size_(size),
756 sourceMap_(source)
757 {
758 setName(name);
759 setType(type);
760 }
761
762 ContainerObj(const ContainerObj& rhs) :
763 ObjTree<ContainerObj>(rhs),
764 size_(rhs.size_),
765 sourceMap_(rhs.sourceMap_)
766 {
767 setName(rhs.name_);
768 setType(rhs.type_);
769 }
770
771 ContainerObj& operator=(const ContainerObj& rhs)
772 {
773 if ( this == &rhs ) return *this;
774 ObjTree<ContainerObj>::operator=(rhs);
775 name_ = rhs.name_;
776 type_ = rhs.type_;
777 size_ = rhs.size_;
778 sourceMap_ = rhs.sourceMap_;
779 return *this;
780 }
781
782 ObjTreeCont* clone() const override { return new ContainerObj(*this); }
783
784
785 const std::string& getContainerType() const { return type_; }
786 size_t getSize() const { return size_; }
787
788 void syncFromSim() override
789 {
790 if ( !sourceMap_ ) return;
791 size_ = sourceMap_->getVariables().size(); // or cheaper accessor if available
792 }
793
794 bool hasChanged() override
795 {
796 if ( !sourceMap_ ) return false;
797 return size_ != sourceMap_->getVariables().size();
798 }
799
800 void apply() override { std::cout << "Container: " << name_ << " (" << type_ << ") size=" << size_ << std::endl; }
801
802 void Dump(
803 const int verbosity, std::ios_base::fmtflags base = std::ios_base::dec, std::ostream& os = std::cout) override
804 {
805 if ( verbosity == 0 ) {
806 os << name_ << " [" << size_ << " elements] (" << type_ << ")" << std::endl;
807 }
808 else {
809 os << name_ << " [" << size_ << " elements] (" << type_ << ")" << std::endl;
810 applyRecursive([&](ObjTreeCont* child) { child->Dump(verbosity - 1, base, os); });
811 }
812 }
813
814 ObjTreeCont* getElementAt(std::vector<size_t> indices) const
815 {
816 ObjTreeCont* current = const_cast<ContainerObj*>(this);
817
818 for ( size_t idx : indices ) {
819 if ( !current ) return nullptr;
820
821 auto& elems = current->getChildren();
822 if ( idx >= elems.size() ) return nullptr;
823
824 current = elems[idx].get();
825 }
826 return current;
827 }
828
829 void printElementAt(std::vector<size_t> indices, int verbosity = 1,
830 std::ios_base::fmtflags base = std::ios_base::dec, std::ostream& os = std::cout) const
831 {
832 ObjTreeCont* elem = getElementAt(indices);
833 if ( elem ) {
834 elem->Dump(verbosity, base, os);
835 }
836 else {
837 os << "Element not found at path {";
838 bool first = true;
839 for ( auto i : indices ) {
840 if ( !first ) os << ", ";
841 os << i;
842 first = false;
843 }
844 os << "}" << std::endl;
845 }
846 }
847
848 bool setElementFromString(std::vector<size_t> indices, std::string val)
849 {
850 ObjTreeCont* elem = getElementAt(indices);
851 bool success = elem != nullptr;
852 if ( elem ) {
853 success = elem->setFromString(val);
854 }
855 if ( !success ) {
856 std::cout << "Element not found at path {";
857 bool first = true;
858 for ( auto i : indices ) {
859 if ( !first ) std::cout << ", ";
860 std::cout << i;
861 first = false;
862 }
863 std::cout << "}" << std::endl;
864 }
865 return success;
866 }
867};
868
869// Node for string types (treated specially since they're fundamental-like)
870class StringObj : public ObjTree<StringObj>
871{
872 std::string val_;
873 void* addr_ = nullptr;
874
875public:
876 StringObj(const std::string& v, void* addr) :
877 ObjTree<StringObj>(NodeKind::String),
878 val_(v),
879 addr_(addr)
880 {}
881 StringObj(const StringObj& rhs) :
882 ObjTree<StringObj>(rhs),
883 val_(rhs.val_),
884 addr_(rhs.addr_)
885 {}
886
887 StringObj& operator=(const StringObj& rhs)
888 {
889 if ( this == &rhs ) return *this;
890 ObjTree<StringObj>::operator=(rhs);
891 val_ = rhs.val_;
892 addr_ = rhs.addr_;
893 return *this;
894 }
895
896 ObjTreeCont* clone() const override { return new StringObj(*this); }
897
898 const std::string& getVal() const { return val_; }
899 void setVal(const std::string& v) { val_ = v; }
900 void setSimVal(const std::string& v)
901 {
902 val_ = v;
903 *static_cast<std::string*>(addr_) = val_;
904 }
905 bool setFromString(const std::string& value) override
906 {
907 if ( readOnly_ ) {
908 std::cout << "WARNING Cannot set ReadOnly Obj " << name_ << std::endl;
909 return false;
910 }
911 setSimVal(value);
912 return true;
913 }
914 virtual void syncFromSim() override { val_ = *static_cast<std::string*>(addr_); }
915 virtual bool hasChanged() override
916 {
917 if ( !addr_ ) return false;
918 return val_ != *static_cast<std::string*>(addr_);
919 }
920
921 void apply() override { std::cout << "Processing string: " << val_ << std::endl; }
922
923 void Dump(const int verbosity, [[maybe_unused]] std::ios_base::fmtflags base = std::ios_base::dec,
924 std::ostream& os = std::cout) override
925 {
926 std::string ro = (readOnly_) ? " (ro)" : "";
927 if ( verbosity == 0 ) {
928 os << getObjName() << ro << std::endl;
929 }
930 else if ( verbosity == 1 ) {
931 os << getObjName() << ro << " = \"" << val_ << "\"" << std::endl;
932 }
933 else {
934 os << getObjName() << ro << " = \"" << val_ << "\"" << " (" << getType() << ")" << std::endl;
935 }
936 }
937};
938
939// Node for bool (separate from integer for clarity)
940class BoolObj : public ObjTree<BoolObj>
941{
942private:
943 bool val_;
944 void* addr_ = nullptr; // This points back to the ObjectMap
945 std::function<bool()> getter_; // accessor-callback mode. these are used to access bitset and vector<bool> types
946 std::function<void(bool)> setter_; // These will be set by ObjectMapFundamentalReference if needed
947 bool useAccessor_ = false; // control if we use addr_ to update the value from the sim or do we use getter_/setter_
948
949public:
950 BoolObj(bool v, void* addr) :
951 ObjTree<BoolObj>(NodeKind::Bool),
952 val_(v),
953 addr_(addr)
954 {}
955 BoolObj(std::function<bool()> getter, std::function<void(bool)> setter) :
956 ObjTree<BoolObj>(NodeKind::Bool),
957 getter_(std::move(getter)),
958 setter_(std::move(setter)),
959 useAccessor_(true)
960 {
961 if ( getter_ ) val_ = getter_();
962 if ( !setter_ ) readOnly_ = true;
963 }
964
965 BoolObj(const BoolObj& rhs) :
966 ObjTree<BoolObj>(rhs),
967 val_(rhs.val_),
968 addr_(rhs.addr_),
969 getter_(rhs.getter_),
970 setter_(rhs.setter_),
971 useAccessor_(rhs.useAccessor_)
972 {}
973
974 BoolObj& operator=(const BoolObj& rhs)
975 {
976 if ( this == &rhs ) return *this;
977 ObjTree<BoolObj>::operator=(rhs);
978 val_ = rhs.val_;
979 addr_ = rhs.addr_;
980 getter_ = rhs.getter_;
981 setter_ = rhs.setter_;
982 useAccessor_ = rhs.useAccessor_;
983 return *this;
984 }
985
986 ObjTreeCont* clone() const override { return new BoolObj(*this); }
987
988 bool getVal() const { return val_; }
989 void setVal(bool v) { val_ = v; }
990 void setSimVal(bool v)
991 {
992 if ( readOnly_ ) {
993 return;
994 }
995 if ( useAccessor_ ) {
996 if ( setter_ ) {
997 setter_(v);
998 }
999 }
1000 else if ( addr_ ) {
1001 *(static_cast<bool*>(addr_)) = v;
1002 val_ = v;
1003 }
1004 }
1005 bool setFromString(const std::string& value) override
1006 {
1007 if ( readOnly_ ) {
1008 std::cout << "WARNING Cannot set ReadOnly Obj " << name_ << std::endl;
1009 return false;
1010 }
1011 setSimVal(value == "true" || value == "1");
1012 return true;
1013 }
1014 virtual void syncFromSim() override
1015 {
1016 if ( useAccessor_ && getter_ ) {
1017 val_ = getter_();
1018 }
1019 else if ( addr_ ) {
1020 val_ = *(static_cast<bool*>(addr_));
1021 }
1022 }
1023 virtual bool hasChanged() override
1024 {
1025 if ( useAccessor_ && getter_ ) {
1026 return val_ != getter_();
1027 }
1028 if ( addr_ ) {
1029 return val_ != *static_cast<bool*>(addr_);
1030 }
1031 return false;
1032 }
1033
1034 void apply() override { std::cout << "Processing bool: " << (val_ ? "true" : "false") << std::endl; }
1035
1036 void Dump(const int verbosity, [[maybe_unused]] std::ios_base::fmtflags base = std::ios_base::dec,
1037 std::ostream& os = std::cout) override
1038 {
1039 std::string ro = (readOnly_) ? " (ro)" : "";
1040 if ( verbosity == 0 ) {
1041 os << getObjName() << ro << std::endl;
1042 }
1043 else if ( verbosity == 1 ) {
1044 os << getObjName() << ro << " = " << (val_ ? "true" : "false") << std::endl;
1045 }
1046 else {
1047 os << getObjName() << ro << " = " << (val_ ? "true" : "false") << " (" << getType() << ")" << std::endl;
1048 }
1049 }
1050};
1051
1052class GenericValObj : public ObjTree<GenericValObj>
1053{
1054 std::string val_;
1055 void* addr_ = nullptr;
1056 ObjectMap* sourceMap_ = nullptr; // for write-back via string interface
1057
1058public:
1059 GenericValObj(const std::string& val, void* addr, ObjectMap* source) :
1060 ObjTree<GenericValObj>(NodeKind::GenericVal),
1061 val_(val),
1062 addr_(addr),
1063 sourceMap_(source)
1064 {}
1065
1066 GenericValObj(const GenericValObj& rhs) :
1067 ObjTree<GenericValObj>(rhs),
1068 val_(rhs.val_),
1069 addr_(rhs.addr_),
1070 sourceMap_(rhs.sourceMap_)
1071 {}
1072
1073 GenericValObj& operator=(const GenericValObj& rhs)
1074 {
1075 if ( this == &rhs ) return *this;
1076 ObjTree<GenericValObj>::operator=(rhs);
1077 val_ = rhs.val_;
1078 addr_ = rhs.addr_;
1079 sourceMap_ = rhs.sourceMap_;
1080 return *this;
1081 }
1082
1083 ObjTreeCont* clone() const override { return new GenericValObj(*this); }
1084
1085 const std::string& getVal() const { return val_; }
1086
1087 // Write-back through ObjectMap's string-based set interface,
1088 // which knows the real type and handles conversion internally.
1089 bool setFromString(const std::string& value) override
1090 {
1091 if ( readOnly_ ) {
1092 std::cout << "WARNING Cannot set ReadOnly Obj " << name_ << std::endl;
1093 return false;
1094 }
1095 if ( !sourceMap_ ) return false;
1096 if ( sourceMap_->isReadOnly() ) return false;
1097 sourceMap_->set(value);
1098 // Re-read to confirm the value was accepted
1099 val_ = sourceMap_->get();
1100 return true;
1101 }
1102
1103 // NOTE: we are currently not carrying around sourceMap so this
1104 // will ALWAYS return false
1105 bool hasChanged() override
1106 {
1107 if ( !sourceMap_ ) return false;
1108 return val_ != sourceMap_->get();
1109 }
1110
1111 void apply() override { std::cout << "Processing generic: " << val_ << std::endl; }
1112
1113 void Dump(const int verbosity, [[maybe_unused]] std::ios_base::fmtflags base = std::ios_base::dec,
1114 std::ostream& os = std::cout) override
1115 {
1116 std::string ro = (readOnly_) ? " (ro)" : "";
1117 if ( verbosity == 0 ) {
1118 os << getObjName() << ro << std::endl;
1119 }
1120 else if ( verbosity == 1 ) {
1121 os << getObjName() << ro << " = " << val_ << std::endl;
1122 }
1123 else {
1124 os << getObjName() << ro << " = " << val_ << " (" << getType() << ")" << std::endl;
1125 }
1126 }
1127};
1128
1129} // namespace SST::Core::Serialization
1130
1131#endif
Definition componentInfo.h:285
Main component object for the simulation.
Definition component.h:32
virtual bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:1023
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:1036
virtual void syncFromSim() override
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:1014
bool setFromString(const std::string &value) override
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:1005
Definition ObjTreeLeaves.h:748
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:802
bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:794
void syncFromSim() override
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:788
virtual void syncFromSim() override
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:643
virtual bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:654
bool setFromString(const std::string &value) override
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:721
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:685
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:1113
bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:1105
bool setFromString(const std::string &value) override
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:1089
virtual void syncFromSim() override
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:474
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:515
bool setFromString(const std::string &value) override
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:545
virtual bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:485
The Object Tree (ObjTree) is intended to be a developer friendly interface to SST’s existing serializ...
Definition ObjTreeLeaves.h:41
void addChildObj(ObjTreeCont *obj)
Adds a child node to the current node.
Definition ObjTreeLeaves.h:103
ObjectType * findByType(Func &&predicate)
Find the first instance of a particular type in this node's children_ that matches criteria set by pr...
Definition ObjTreeLeaves.h:205
virtual void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout)
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:248
virtual bool setFromString(const std::string &value)
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:265
ObjTreeCont * findByName(const std::string name)
Find the first instance of a child with name_ matching name.
Definition ObjTreeLeaves.h:223
void makeReadOnly()
Function that will be called if during conversion from ObjectMap the node has been marked as ReadOnly...
Definition ObjTreeLeaves.h:141
void setType(const std::string &type)
Set the type of the node - this comes from ObjectMap/Serialization.
Definition ObjTreeLeaves.h:124
bool isRoot()
Returns true if this is the top of the tree - determined by the parent pointer being nullptr.
Definition ObjTreeLeaves.h:134
void applyRecursive(Func &&func)
Apply a function to all children of this node regardless of type, etc.
Definition ObjTreeLeaves.h:153
virtual bool hasChanged()
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:280
virtual void syncFromSim()
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:271
void applyRecursiveByType(Func &&func)
Apply a function to all children of this node of a specific type.
Definition ObjTreeLeaves.h:169
virtual void clear()
Clears the entire Object Tree and frees all nodes.
Definition ObjTreeLeaves.h:293
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:385
void BuildTree(const ComponentInfoMap &compMap)
Each time we break into the debugger we rebuild the ObjectTree based on the current ComponentInfoMap.
Definition ObjTree.h:114
void clear() override
Clears the entire Object Tree and frees all nodes.
Definition ObjTreeLeaves.h:394
Base class for objects created by the serializer mapping mode used to map the variables for objects.
Definition objectMap.h:189
virtual void syncFromSim() override
This function will update the local stored node value with the live value in the sst simulation.
Definition ObjTreeLeaves.h:914
void Dump(const int verbosity, std::ios_base::fmtflags base=std::ios_base::dec, std::ostream &os=std::cout) override
Print the contents of the node to the stream specified by os.
Definition ObjTreeLeaves.h:923
bool setFromString(const std::string &value) override
Set the value stored in the node from a string.
Definition ObjTreeLeaves.h:905
virtual bool hasChanged() override
This function will compare the stored the node value with the live simulation value.
Definition ObjTreeLeaves.h:915