SST 16.1.0
Structural Simulation Toolkit
serialize_insertable.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_IMPL_SERIALIZE_INSERTABLE_H
13#define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_INSERTABLE_H
14
15#ifndef SST_INCLUDING_SERIALIZE_H
16#warning \
17 "The header file sst/core/serialization/impl/serialize_insertable.h should not be directly included as it is not part of the stable public API. The file is included in sst/core/serialization/serialize.h"
18#endif
19
20#include "sst/core/serialization/serializer.h"
21
22#include <cstddef>
23#include <deque>
24#include <forward_list>
25#include <iterator>
26#include <list>
27#include <map>
28#include <set>
29#include <string>
30#include <type_traits>
31#include <unordered_map>
32#include <unordered_set>
33#include <utility>
34#include <vector>
35
36namespace SST::Core::Serialization {
37
38// If the type is a pair with a const first, map it to pair with non-const first
39template <typename T>
41{
42 using type = T;
43};
44
45template <typename KEY, typename VALUE>
46struct remove_const_key<std::pair<const KEY, VALUE>>
47{
48 using type = std::pair<KEY, VALUE>;
49};
50
51// Whether a size() method exists for a type
52template <typename, typename = void>
53constexpr bool has_size_v = false;
54
55template <typename T>
56constexpr bool has_size_v<T, std::void_t<decltype(std::declval<T>().size())>> = true;
57
58// Whether std::distance(begin(), end()) exists for a type
59template <typename, typename = void>
60constexpr bool has_distance_v = false;
61
62template <typename T>
63constexpr bool
64 has_distance_v<T, std::void_t<decltype(std::distance(std::declval<T>().begin(), std::declval<T>().end()))>> = true;
65
66// Get the size of a container, using a size() method if it exists, otherwise distance(begin, end)
67template <typename T>
68std::enable_if_t<has_size_v<T> || has_distance_v<T>, size_t>
69get_size(const T& t)
70{
71 if constexpr ( has_size_v<T> )
72 return t.size();
73 else
74 return std::distance(t.begin(), t.end());
75}
76
77// Whether it is a std::vector<bool>
78template <typename>
79constexpr bool is_vector_bool_v = false;
80
81template <typename... Ts>
82constexpr bool is_vector_bool_v<std::vector<bool, Ts...>> = true;
83
84// Whether it is a simple map (not a multimap and has integral, floating-point, enum, or convertible to string keys)
85template <typename>
86constexpr bool is_simple_map_v = false;
87
88template <template <typename...> class MAP, typename KEY, typename... REST>
89constexpr bool is_simple_map_v<MAP<KEY, REST...>> =
90 (is_same_template_v<MAP, std::map> || is_same_template_v<MAP, std::unordered_map>) &&
91 (std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>);
92
93
94// Whether it is a simple set (not a multiset and has integral, floating-point, enum, or convertible to string keys)
95template <typename>
96constexpr bool is_simple_set_v = false;
97
98template <template <typename...> class SET, typename KEY, typename... REST>
99constexpr bool is_simple_set_v<SET<KEY, REST...>> =
100 (is_same_template_v<SET, std::set> || is_same_template_v<SET, std::unordered_set>) &&
101 (std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>);
102
103// Whether a type is an insertable container type
104//
105// std::deque
106// std::forward_list
107// std::list
108// std::map
109// std::multimap
110// std::multiset
111// std::set
112// std::unordered_map
113// std::unordered_multimap
114// std::unordered_multiset
115// std::unordered_set
116// std::vector, including std::vector<bool>
117//
118// clang-format off
119template <typename T>
120constexpr bool is_insertable_v = std::disjunction_v<
121 is_same_type_template<T, std::deque>,
122 is_same_type_template<T, std::forward_list>,
123 is_same_type_template<T, std::list>,
124 is_same_type_template<T, std::map>,
125 is_same_type_template<T, std::multimap>,
126 is_same_type_template<T, std::multiset>,
127 is_same_type_template<T, std::set>,
128 is_same_type_template<T, std::unordered_map>,
129 is_same_type_template<T, std::unordered_multimap>,
130 is_same_type_template<T, std::unordered_multiset>,
131 is_same_type_template<T, std::unordered_set>,
132 is_same_type_template<T, std::vector> >;
133// clang-format on
134
135template <typename OBJ>
136class serialize_impl<OBJ, std::enable_if_t<is_insertable_v<OBJ>>>
137{
138 // Value type of element with const removed from first of pair if it exists
139 using value_type = typename remove_const_key<typename OBJ::value_type>::type;
140
141 void operator()(OBJ& obj, serializer& ser, ser_opt_t options)
142 {
143 // Options to use per-element
144 const ser_opt_t UNUSED(opts) =
145 SerOption::is_set(options, SerOption::as_ptr_elem) ? SerOption::as_ptr : SerOption::none;
146
147 switch ( const auto mode = ser.mode() ) {
148 case serializer::SIZER:
149 case serializer::PACK:
150 {
151 size_t size = get_size(obj);
152
153 if ( mode == serializer::PACK )
154 ser.pack(size);
155 else
156 ser.size(size);
157
158 if constexpr ( is_vector_bool_v<OBJ> ) {
159 // For std::vector<bool>, iterate over bool values instead of references to elements.
160 for ( bool e : obj )
161 SST_SER(e); // as_ptr_elem not valid for bool
162 }
163 else if constexpr ( is_same_type_template_v<OBJ, std::map> ||
164 is_same_type_template_v<OBJ, std::unordered_map> ||
165 is_same_type_template_v<OBJ, std::multimap> ||
166 is_same_type_template_v<OBJ, std::unordered_multimap> ) {
167
168 // Maps have to be treated differently in the case of the as_ptr_elem option
169 for ( auto& e : obj ) {
170 auto& cast_e = const_cast<value_type&>(reinterpret_cast<const value_type&>(e));
171 SST_SER_NAME(cast_e.first, "key");
172 SST_SER_NAME(cast_e.second, "value", opts);
173 }
174 }
175 else {
176 // Iterate over references to elements, casting away any const in keys
177 for ( auto& e : obj )
178 SST_SER(const_cast<value_type&>(reinterpret_cast<const value_type&>(e)), opts);
179 }
180 break;
181 }
182
183 case serializer::UNPACK:
184 {
185 // Get the total size of the container
186 size_t size {};
187 ser.unpack(size);
188
189 // Erase the container
190 obj.clear();
191
192 if constexpr ( is_same_type_template_v<OBJ, std::vector> ) obj.reserve(size); // Reserve size of vector
193
194 if constexpr ( is_same_type_template_v<OBJ, std::forward_list> ) {
195 auto last = obj.before_begin(); // iterator of last element inserted
196 for ( size_t i = 0; i < size; ++i ) {
197 last = obj.emplace_after(last);
198 auto& value = *last;
199 SST_SER(value, opts);
200 }
201 }
202 else {
203 for ( size_t i = 0; i < size; ++i ) {
204 if constexpr ( is_same_type_template_v<OBJ, std::map> ||
205 is_same_type_template_v<OBJ, std::unordered_map> ) {
206 typename OBJ::key_type key {};
207 SST_SER(key);
208 auto& value = obj[std::move(key)];
209 SST_SER(value, opts);
210 }
211 else if constexpr ( is_same_type_template_v<OBJ, std::multimap> ||
212 is_same_type_template_v<OBJ, std::unordered_multimap> ) {
213 typename OBJ::key_type key {};
214 SST_SER(key);
215 auto& value = obj.emplace(std::move(key), typename OBJ::mapped_type {})->second;
216 SST_SER(value, opts);
217 }
218 else if constexpr ( is_same_type_template_v<OBJ, std::set> ||
219 is_same_type_template_v<OBJ, std::unordered_set> ||
220 is_same_type_template_v<OBJ, std::multiset> ||
221 is_same_type_template_v<OBJ, std::unordered_multiset> ) {
222 typename OBJ::key_type key {};
223 // TODO: Figure out how to make as_ptr_elem work with sets
224 SST_SER(key);
225 obj.emplace(std::move(key));
226 }
227 else if constexpr ( is_vector_bool_v<OBJ> ) {
228 bool value {};
229 SST_SER(value);
230 obj.push_back(value);
231 }
232 else { // std::vector, std::deque, std::list
233 auto& value = obj.emplace_back();
234 SST_SER(value, opts);
235 }
236 }
237 }
238 // assert(size == get_size(obj));
239 break;
240 }
241
242 case serializer::MAP:
243 {
244 const std::string& name = ser.getMapName();
245 ser.mapper().map_hierarchy_start(name, new ObjectMapContainer<OBJ>(&obj));
246
247 if constexpr ( is_vector_bool_v<OBJ> ) {
248 // std::vector<bool>
249 // Serialize reference wrappers to each bit.
250 for ( size_t i = 0; i < obj.size(); ++i )
251 SST_SER_NAME(pvt::bit_reference_wrapper<OBJ>(obj[i]), std::to_string(i).c_str());
252 }
253 else if constexpr ( is_simple_map_v<OBJ> ) {
254 // non-multi maps with a simple key
255 using SST::Core::to_string;
256 for ( auto& [key, value] : obj )
257 SST_SER_NAME(value, to_string(key).c_str());
258 }
259 // TODO: handle is_simple_set
260 else {
261 // std::vector, std::deque, std::list, std::forward_list, std::multimap,
262 // std::unordered_multimap, std::multiset, std::unordered_multiset, and
263 // std::map, std::set, std::unordered_map std::unordered_set with non-simple keys
264 size_t i = 0;
265 for ( auto& e : obj )
266 SST_SER_NAME(
267 const_cast<value_type&>(reinterpret_cast<const value_type&>(e)), std::to_string(i++).c_str());
268 }
269 ser.mapper().map_hierarchy_end();
270 break;
271 }
272 }
273 }
274
275 SST_FRIEND_SERIALIZE();
276};
277
278template <typename OBJ>
279class serialize_impl<OBJ*, std::enable_if_t<is_insertable_v<OBJ>>>
280{
281 void operator()(OBJ*& obj, serializer& ser, ser_opt_t options)
282 {
283 if ( ser.mode() == serializer::UNPACK ) obj = new OBJ;
284 SST_SER(*obj, options);
285 }
286
287 SST_FRIEND_SERIALIZE();
288};
289
290// Serialize a std::vector<bool> bit using the pvt::bit_reference_wrapper<std::vector<bool>>
291// This is only used in mapping mode
292template <typename ALLOC>
293class serialize_impl<pvt::bit_reference_wrapper<std::vector<bool, ALLOC>>>
294{
295 void operator()(pvt::bit_reference_wrapper<std::vector<bool, ALLOC>>& t, serializer& ser, ser_opt_t UNUSED(options))
296 {
297 ser.mapper().map_hierarchy_start(ser.getMapName(),
298 new ObjectMapFundamentalReference<bool, typename std::vector<bool, ALLOC>::reference>(t.ref));
299 ser.mapper().map_hierarchy_end();
300 }
301 SST_FRIEND_SERIALIZE();
302};
303
304
305} // namespace SST::Core::Serialization
306
307#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_INSERTABLE_H
Class used to map containers.
Definition objectMap.h:1440
Base serialize class.
Definition serialize.h:133
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition serializer.h:43
Definition serialize_insertable.h:41