SST 16.0.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// Whether it is a simple set (not a multiset and has integral, floating-point, enum, or convertible to string keys)
94template <typename>
95constexpr bool is_simple_set_v = false;
96
97template <template <typename...> class SET, typename KEY, typename... REST>
98constexpr bool is_simple_set_v<SET<KEY, REST...>> =
99 (is_same_template_v<SET, std::set> || is_same_template_v<SET, std::unordered_set>) &&
100 (std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>);
101
102// Whether a type is an insertable container type
103//
104// std::deque
105// std::forward_list
106// std::list
107// std::map
108// std::multimap
109// std::multiset
110// std::set
111// std::unordered_map
112// std::unordered_multimap
113// std::unordered_multiset
114// std::unordered_set
115// std::vector, including std::vector<bool>
116//
117// clang-format off
118template <typename T>
119constexpr bool is_insertable_v = std::disjunction_v<
120 is_same_type_template<T, std::deque>,
121 is_same_type_template<T, std::forward_list>,
122 is_same_type_template<T, std::list>,
123 is_same_type_template<T, std::map>,
124 is_same_type_template<T, std::multimap>,
125 is_same_type_template<T, std::multiset>,
126 is_same_type_template<T, std::set>,
127 is_same_type_template<T, std::unordered_map>,
128 is_same_type_template<T, std::unordered_multimap>,
129 is_same_type_template<T, std::unordered_multiset>,
130 is_same_type_template<T, std::unordered_set>,
131 is_same_type_template<T, std::vector> >;
132// clang-format on
133
134template <typename OBJ>
135class serialize_impl<OBJ, std::enable_if_t<is_insertable_v<OBJ>>>
136{
137 // Value type of element with const removed from first of pair if it exists
138 using value_type = typename remove_const_key<typename OBJ::value_type>::type;
139
140 void operator()(OBJ& obj, serializer& ser, ser_opt_t options)
141 {
142 // Options to use per-element
143 const ser_opt_t UNUSED(opts) =
144 SerOption::is_set(options, SerOption::as_ptr_elem) ? SerOption::as_ptr : SerOption::none;
145
146 switch ( const auto mode = ser.mode() ) {
147 case serializer::SIZER:
148 case serializer::PACK:
149 {
150 size_t size = get_size(obj);
151
152 if ( mode == serializer::PACK )
153 ser.pack(size);
154 else
155 ser.size(size);
156
157 if constexpr ( is_vector_bool_v<OBJ> ) {
158 // For std::vector<bool>, iterate over bool values instead of references to elements.
159 for ( bool e : obj )
160 SST_SER(e); // as_ptr_elem not valid for bool
161 }
162 else {
163 // Iterate over references to elements, casting away any const in keys
164 for ( auto& e : obj )
165 SST_SER(const_cast<value_type&>(reinterpret_cast<const value_type&>(e)), opts);
166 }
167 break;
168 }
169
170 case serializer::UNPACK:
171 {
172 // Get the total size of the container
173 size_t size {};
174 ser.unpack(size);
175
176 // Erase the container
177 obj.clear();
178
179 if constexpr ( is_same_type_template_v<OBJ, std::vector> ) obj.reserve(size); // Reserve size of vector
180
181 if constexpr ( is_same_type_template_v<OBJ, std::forward_list> ) {
182 auto last = obj.before_begin(); // iterator of last element inserted
183 for ( size_t i = 0; i < size; ++i ) {
184 last = obj.emplace_after(last);
185 auto& value = *last;
186 SST_SER(value, opts);
187 }
188 }
189 else {
190 for ( size_t i = 0; i < size; ++i ) {
191 if constexpr ( is_same_type_template_v<OBJ, std::map> ||
192 is_same_type_template_v<OBJ, std::unordered_map> ) {
193 typename OBJ::key_type key {};
194 SST_SER(key);
195 auto& value = obj[std::move(key)];
196 SST_SER(value, opts);
197 }
198 else if constexpr ( is_same_type_template_v<OBJ, std::multimap> ||
199 is_same_type_template_v<OBJ, std::unordered_multimap> ) {
200 typename OBJ::key_type key {};
201 SST_SER(key);
202 auto& value = obj.emplace(std::move(key), typename OBJ::mapped_type {})->second;
203 SST_SER(value, opts);
204 }
205 else if constexpr ( is_same_type_template_v<OBJ, std::set> ||
206 is_same_type_template_v<OBJ, std::unordered_set> ||
207 is_same_type_template_v<OBJ, std::multiset> ||
208 is_same_type_template_v<OBJ, std::unordered_multiset> ) {
209 typename OBJ::key_type key {};
210 // TODO: Figure out how to make as_ptr_elem work with sets
211 SST_SER(key);
212 obj.emplace(std::move(key));
213 }
214 else if constexpr ( is_vector_bool_v<OBJ> ) {
215 bool value {};
216 SST_SER(value);
217 obj.push_back(value);
218 }
219 else { // std::vector, std::deque, std::list
220 auto& value = obj.emplace_back();
221 SST_SER(value, opts);
222 }
223 }
224 }
225 // assert(size == get_size(obj));
226 break;
227 }
228
229 case serializer::MAP:
230 {
231 const std::string& name = ser.getMapName();
232 ser.mapper().map_hierarchy_start(name, new ObjectMapContainer<OBJ>(&obj));
233
234 if constexpr ( is_vector_bool_v<OBJ> ) {
235 // std::vector<bool>
236 // Serialize reference wrappers to each bit.
237 for ( size_t i = 0; i < obj.size(); ++i )
238 SST_SER_NAME(pvt::bit_reference_wrapper<OBJ>(obj[i]), std::to_string(i).c_str());
239 }
240 else if constexpr ( is_simple_map_v<OBJ> ) {
241 // non-multi maps with a simple key
242 using SST::Core::to_string;
243 for ( auto& [key, value] : obj )
244 SST_SER_NAME(value, to_string(key).c_str());
245 }
246 // TODO: handle is_simple_set
247 else {
248 // std::vector, std::deque, std::list, std::forward_list, std::multimap,
249 // std::unordered_multimap, std::multiset, std::unordered_multiset, and
250 // std::map, std::set, std::unordered_map std::unordered_set with non-simple keys
251 size_t i = 0;
252 for ( auto& e : obj )
253 SST_SER_NAME(
254 const_cast<value_type&>(reinterpret_cast<const value_type&>(e)), std::to_string(i++).c_str());
255 }
256 ser.mapper().map_hierarchy_end();
257 break;
258 }
259 }
260 }
261
262 SST_FRIEND_SERIALIZE();
263};
264
265template <typename OBJ>
266class serialize_impl<OBJ*, std::enable_if_t<is_insertable_v<OBJ>>>
267{
268 void operator()(OBJ*& obj, serializer& ser, ser_opt_t options)
269 {
270 if ( ser.mode() == serializer::UNPACK ) obj = new OBJ;
271 SST_SER(*obj, options);
272 }
273
274 SST_FRIEND_SERIALIZE();
275};
276
277// Serialize a std::vector<bool> bit using the pvt::bit_reference_wrapper<std::vector<bool>>
278// This is only used in mapping mode
279template <typename ALLOC>
280class serialize_impl<pvt::bit_reference_wrapper<std::vector<bool, ALLOC>>>
281{
282 void operator()(pvt::bit_reference_wrapper<std::vector<bool, ALLOC>>& t, serializer& ser, ser_opt_t UNUSED(options))
283 {
284 ser.mapper().map_hierarchy_start(ser.getMapName(),
285 new ObjectMapFundamentalReference<bool, typename std::vector<bool, ALLOC>::reference>(t.ref));
286 ser.mapper().map_hierarchy_end();
287 }
288 SST_FRIEND_SERIALIZE();
289};
290
291
292} // namespace SST::Core::Serialization
293
294#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_INSERTABLE_H
Class used to map containers.
Definition objectMap.h:1420
Base serialize class.
Definition serialize.h:132
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