SST 16.1.0
Structural Simulation Toolkit
NumericHandle.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#include "sst/core/serialization/ObjTree.h"
13
14#include <cstdint>
15#include <type_traits>
16
17namespace SST::Core::Serialization {
18// A lightweight non-owning handle returned by findByName that enables
19// direct comparisons against constants and other tree nodes.
20//
21// Usage:
22// NumericHandle a = NumericHandle::from(root->findByName("counter"));
23// NumericHandle b = NumericHandle::from(root->findByName("threshold"));
24//
25// if (a < 42) { ... } // compare against int constant
26// if (a < 3.14) { ... } // compare against double constant
27// if (a < b) { ... } // compare two tree nodes
28
29class NumericHandle
30{
31public:
32 enum class Kind { None, Integer, Float, Bool, Cont };
33
34private:
35 ObjTreeCont* node_ = nullptr;
36 Kind kind_ = Kind::None;
37
38 NumericHandle(ObjTreeCont* n, Kind k) :
39 node_(n),
40 kind_(k)
41 {}
42
43public:
44 NumericHandle() = default;
45
46 static NumericHandle from(ObjTreeCont* node)
47 {
48 if ( !node ) return {};
49 if ( dynamic_cast<IntegerObj*>(node) ) return { node, Kind::Integer };
50 if ( dynamic_cast<FloatObj*>(node) ) return { node, Kind::Float };
51 if ( dynamic_cast<BoolObj*>(node) ) return { node, Kind::Bool };
52 if ( dynamic_cast<ContainerObj*>(node) ) return { node, Kind::Cont };
53 return {};
54 }
55
56 explicit operator bool() const { return kind_ != Kind::None; }
57 Kind getKind() const { return kind_; }
58 ObjTreeCont* getNode() const { return node_; }
59
60 IntegerObj* asInteger() const { return dynamic_cast<IntegerObj*>(node_); }
61 FloatObj* asFloat() const { return dynamic_cast<FloatObj*>(node_); }
62 BoolObj* asBool() const { return dynamic_cast<BoolObj*>(node_); }
63 ContainerObj* asCont() const { return dynamic_cast<ContainerObj*>(node_); }
64
65 // ── Compare handle <=> handle ────────────────────────────────
66 bool operator<(const NumericHandle& rhs) const { return cmpHandle(rhs, std::less<> {}); }
67 bool operator>(const NumericHandle& rhs) const { return rhs < *this; }
68 bool operator<=(const NumericHandle& rhs) const { return !(rhs < *this); }
69 bool operator>=(const NumericHandle& rhs) const { return !(*this < rhs); }
70 bool operator==(const NumericHandle& rhs) const { return !(*this < rhs) && !(rhs < *this); }
71 bool operator!=(const NumericHandle& rhs) const { return !(*this == rhs); }
72
73 // ── Compare handle <=> arithmetic constant ───────────────────
74 // A single template for every numeric type avoids the int64_t vs
75 // double ambiguity that plain overloads cause.
76
77 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
78 bool operator<(T rhs) const
79 {
80 return cmpConst(rhs, std::less<> {});
81 }
82
83 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
84 bool operator>(T rhs) const
85 {
86 return cmpConst(rhs, std::greater<> {});
87 }
88
89 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
90 bool operator<=(T rhs) const
91 {
92 return cmpConst(rhs, std::less_equal<> {});
93 }
94
95 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
96 bool operator>=(T rhs) const
97 {
98 return cmpConst(rhs, std::greater_equal<> {});
99 }
100
101 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
102 bool operator==(T rhs) const
103 {
104 return cmpConst(rhs, std::equal_to<> {});
105 }
106
107 template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
108 bool operator!=(T rhs) const
109 {
110 return cmpConst(rhs, std::not_equal_to<> {});
111 }
112
113private:
114 // Visit both sides' variants and compare directly.
115 template <typename Cmp>
116 bool cmpHandle(const NumericHandle& rhs, Cmp cmp) const
117 {
118 if ( !*this || !rhs ) return false;
119
120 // Lambda that visits the RHS given a concrete LHS value
121 auto visitRhs = [&](auto lhsVal) -> bool {
122 auto inner = [&](auto rhsVal) -> bool {
123 if constexpr ( std::is_integral_v<decltype(lhsVal)> && std::is_integral_v<decltype(rhsVal)> ) {
124 using Common = std::common_type_t<decltype(lhsVal), decltype(rhsVal)>;
125 return cmp(static_cast<Common>(lhsVal), static_cast<Common>(rhsVal));
126 }
127 else {
128 return cmp(static_cast<long double>(lhsVal), static_cast<long double>(rhsVal));
129 }
130 };
131 if ( rhs.kind_ == Kind::Integer ) return rhs.asInteger()->visit(inner);
132 if ( rhs.kind_ == Kind::Float ) return rhs.asFloat()->visit(inner);
133 if ( rhs.kind_ == Kind::Bool ) return inner(static_cast<int64_t>(rhs.asBool()->getVal()));
134 if ( rhs.kind_ == Kind::Cont ) return inner(static_cast<int64_t>(rhs.asCont()->getSize()));
135 return false;
136 };
137
138 if ( kind_ == Kind::Integer ) return asInteger()->visit(visitRhs);
139 if ( kind_ == Kind::Float ) return asFloat()->visit(visitRhs);
140 if ( kind_ == Kind::Bool ) return visitRhs(static_cast<int64_t>(asBool()->getVal()));
141 if ( kind_ == Kind::Cont ) return visitRhs(static_cast<int64_t>(asCont()->getSize()));
142 return false;
143 }
144
145 // Visit the held variant and apply `cmp` against any arithmetic constant.
146 // Integer-to-integer comparisons stay exact; anything involving a
147 // floating-point side is promoted to long double to preserve precision.
148 template <typename ConstT, typename Cmp>
149 bool cmpConst(ConstT rhs, Cmp cmp) const
150 {
151 if ( kind_ == Kind::Integer ) {
152 return asInteger()->visit([&](auto lhs) {
153 if constexpr ( std::is_integral_v<decltype(lhs)> && std::is_integral_v<ConstT> ) {
154 // Both sides integral — compare without floating-point loss
155 using Common = std::common_type_t<decltype(lhs), ConstT>;
156 return cmp(static_cast<Common>(lhs), static_cast<Common>(rhs));
157 }
158 else {
159 return cmp(static_cast<long double>(lhs), static_cast<long double>(rhs));
160 }
161 });
162 }
163 if ( kind_ == Kind::Float ) {
164 return asFloat()->visit(
165 [&](auto lhs) { return cmp(static_cast<long double>(lhs), static_cast<long double>(rhs)); });
166 }
167 if ( kind_ == Kind::Bool ) {
168 auto lhs = static_cast<int64_t>(asBool()->getVal());
169 if constexpr ( std::is_integral_v<ConstT> ) {
170 using Common = std::common_type_t<int64_t, ConstT>;
171 return cmp(static_cast<Common>(lhs), static_cast<Common>(rhs));
172 }
173 else {
174 return cmp(static_cast<long double>(lhs), static_cast<long double>(rhs));
175 }
176 }
177 if ( kind_ == Kind::Cont ) {
178 auto lhs = asCont()->getSize();
179 if constexpr ( std::is_integral_v<ConstT> ) {
180 using Common = std::common_type_t<int64_t, ConstT>;
181 return cmp(static_cast<Common>(lhs), static_cast<Common>(rhs));
182 }
183 else {
184 return cmp(static_cast<long double>(lhs), static_cast<long double>(rhs));
185 }
186 }
187 return false;
188 }
189};
190
191// ── Reverse comparisons (constant <=> handle) ───────────────────
192template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
193bool
194operator<(T lhs, const NumericHandle& rhs)
195{
196 return rhs > lhs;
197}
198
199template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
200bool
201operator>(T lhs, const NumericHandle& rhs)
202{
203 return rhs < lhs;
204}
205
206template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
207bool
208operator<=(T lhs, const NumericHandle& rhs)
209{
210 return rhs >= lhs;
211}
212
213template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
214bool
215operator>=(T lhs, const NumericHandle& rhs)
216{
217 return rhs <= lhs;
218}
219}; // namespace SST::Core::Serialization
Definition ObjTreeLeaves.h:941
Definition ObjTreeLeaves.h:748
Definition ObjTreeLeaves.h:572
Definition ObjTreeLeaves.h:405
Definition NumericHandle.h:30
The Object Tree (ObjTree) is intended to be a developer friendly interface to SST’s existing serializ...
Definition ObjTreeLeaves.h:41