SST  15.1.0
StructuralSimulationToolkit
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/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 
36 namespace SST::Core::Serialization {
37 
38 // If the type is a pair with a const first, map it to pair with non-const first
39 template <typename T>
41 {
42  using type = T;
43 };
44 
45 template <typename KEY, typename VALUE>
46 struct 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
52 template <typename, typename = void>
53 constexpr bool has_size_v = false;
54 
55 template <typename T>
56 constexpr 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
59 template <typename, typename = void>
60 constexpr bool has_distance_v = false;
61 
62 template <typename T>
63 constexpr 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)
67 template <typename T>
68 std::enable_if_t<has_size_v<T> || has_distance_v<T>, size_t>
69 get_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>
78 template <typename>
79 constexpr bool is_vector_bool_v = false;
80 
81 template <typename... Ts>
82 constexpr 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)
85 template <typename>
86 constexpr bool is_simple_map_v = false;
87 
88 template <template <typename...> class MAP, typename KEY, typename... REST>
89 constexpr 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)
94 template <typename>
95 constexpr bool is_simple_set_v = false;
96 
97 template <template <typename...> class SET, typename KEY, typename... REST>
98 constexpr 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
118 template <typename T>
119 constexpr 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 
134 template <typename OBJ>
135 class 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  using SST::Core::to_string;
232  const std::string& name = ser.getMapName();
233  ser.mapper().map_hierarchy_start(name, new ObjectMapContainer<OBJ>(&obj));
234 
235  if constexpr ( is_vector_bool_v<OBJ> ) {
236  // std::vector<bool>
237  size_t i = 0;
238  for ( bool e : obj )
239  SST_SER_NAME(e, to_string(i++).c_str());
240  }
241  else if constexpr ( is_simple_map_v<OBJ> ) {
242  // non-multi maps with a simple key
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)), to_string(i++).c_str());
255  }
256  ser.mapper().map_hierarchy_end();
257  break;
258  }
259  }
260  }
261 
262  SST_FRIEND_SERIALIZE();
263 };
264 
265 template <typename OBJ>
266 class 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 } // namespace SST::Core::Serialization
278 
279 #endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_INSERTABLE_H
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:42
Class used to map containers.
Definition: objectMap.h:1457
Base serialize class.
Definition: serialize.h:113
Definition: serialize_insertable.h:40