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