SST  14.0.0
StructuralSimulationToolkit
serialize.h
1 // Copyright 2009-2024 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-2024, 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_SERIALIZE_H
13 #define SST_CORE_SERIALIZATION_SERIALIZE_H
14 
15 #include "sst/core/serialization/serializer.h"
16 #include "sst/core/warnmacros.h"
17 
18 #include <atomic>
19 #include <iostream>
20 #include <typeinfo>
21 
22 namespace SST {
23 namespace Core {
24 namespace Serialization {
25 
26 /**
27  Base serialize class. This is the default, which if hit will
28  static_assert. All other instances are partial specializations of
29  this class and do all the real serialization.
30  */
31 template <class T, class Enable = void>
33 {
34 public:
35  // inline void operator()(T& UNUSED(t), serializer& UNUSED(ser))
36  // {
37  // // If the default gets called, then it's actually invalid
38  // // because we don't know how to serialize it.
39 
40  // // This is a bit strange, but if I just do a
41  // // static_assert(false) it always triggers, but if I use
42  // // std::is_* then it seems to only trigger if something expands
43  // // to this version of the template.
44  // // static_assert(false,"Trying to serialize an object that is not serializable.");
45  // static_assert(std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
46  // static_assert(!std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
47  // }
48 
49  inline void operator()(T& t, serializer& ser)
50  {
51  // This is the fall through case. Check to see if it's a pointer:
52  if constexpr ( std::is_pointer_v<T> ) {
53  // If it falls through to the default, let's check to see if it's
54  // a non-polymorphic class and try to call serialize_order
55  if constexpr ( std::is_class_v<std::remove_pointer<T>> && !std::is_polymorphic_v<std::remove_pointer<T>> ) {
56  if ( ser.mode() == serializer::UNPACK ) {
57  t = new typename std::remove_pointer<T>::type();
58  ser.report_new_pointer(reinterpret_cast<uintptr_t>(t));
59  }
60  t->serialize_order(ser);
61  }
62  else {
63  static_assert(std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
64  static_assert(
65  !std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
66  }
67  }
68  else {
69  // If it falls through to the default, let's check to see if it's
70  // a non-polymorphic class and try to call serialize_order
71  if constexpr ( std::is_class_v<T> && !std::is_polymorphic_v<T> ) { t.serialize_order(ser); }
72  else {
73  static_assert(std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
74  static_assert(
75  !std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
76  }
77  }
78  }
79 };
80 
81 
82 // template <class T>
83 // class serialize_impl<T*,std::enable_if_t<std::is_base_of<SST::Core::Serialization::serializable, T>::value, bool> =
84 // true>
85 // {
86 // public:
87 // inline void operator()(T& t, serializer& ser)
88 // {
89 // // If it falls through to the default, let's check to see if it's
90 // // a non-polymorphic class and try to call serialize_order
91 // if constexpr ( std::is_class_v<T> && !std::is_polymorphic_v<T> /*&& std::is_same_v<typename
92 // MethodDetect<decltype( T::serialize_order() )>::type, void>*/) {
93 // t.serialize_order(ser);
94 // }
95 // else {
96 // static_assert(std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
97 // static_assert(!std::is_fundamental<T>::value, "Trying to serialize an object that is not serializable.");
98 // }
99 // }
100 // };
101 
102 
103 /**
104  Serialization "gateway" object. All serializations must come
105  thorugh these template instances in order for pointer tracking to
106  be controlled at one point. The actual serialization will happen in
107  serialize_impl classes.
108  */
109 template <class T>
111 {
112 public:
113  inline void operator()(T& t, serializer& ser) { return serialize_impl<T>()(t, ser); }
114 
115  /**
116  This will track the pointer to the object if pointer tracking
117  is turned on. Otherwise, it will just serialize normally. This
118  is only useful if you have a non-pointer struct/class where
119  other pieces of code will have pointers to it (e.g. a set/map
120  contains the actual struct and other places point to the data's
121  location)
122  */
123  inline void serialize_and_track_pointer(T& t, serializer& ser)
124  {
125  if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T>()(t, ser);
126 
127  // Get the pointer to the data that will be uesd for tracking.
128  uintptr_t ptr = reinterpret_cast<uintptr_t>(&t);
129 
130  switch ( ser.mode() ) {
131  case serializer::SIZER:
132  // Check to see if the pointer has already been seen. If
133  // so, then we error since the non-pointer version of this
134  // data needs to be serialized before any of the pointers
135  // that point to it.
136  if ( ser.check_pointer_pack(ptr) ) {
137  // Error
138  }
139 
140  // Always put the pointer in
141  ser.size(ptr);
142  // Count the data for the object
143  serialize_impl<T>()(t, ser);
144  break;
145  case serializer::PACK:
146  // Already checked serialization order during sizing, so
147  // don't need to do it here
148  ser.check_pointer_pack(ptr);
149  // Always put the pointer in
150  ser.pack(ptr);
151  // Serialize the data for the object
152  serialize_impl<T>()(t, ser);
153  break;
154  case serializer::UNPACK:
155  // Checked order on serialization, so don't need to do it
156  // here
157 
158  // Get the stored pointer to use as a tag for future
159  // references to the data
160  uintptr_t ptr_stored;
161  ser.unpack(ptr_stored);
162 
163  // Now add this to the tracking data
164  ser.report_real_pointer(ptr_stored, ptr);
165 
166  serialize_impl<T>()(t, ser);
167  }
168  }
169 };
170 
171 template <class T>
172 class serialize<T*>
173 {
174 public:
175  inline void operator()(T*& t, serializer& ser)
176  {
177  // We are a pointer, need to see if tracking is turned on
178  if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T*>()(t, ser);
179 
180  uintptr_t ptr = reinterpret_cast<uintptr_t>(t);
181  if ( nullptr == t ) ptr = 0;
182 
183  switch ( ser.mode() ) {
184  case serializer::SIZER:
185  // Always put the pointer in
186  ser.size(ptr);
187 
188  // If this is a nullptr, then we are done
189  if ( 0 == ptr ) return;
190 
191  // If we haven't seen this yet, also need to serialize the
192  // object
193  if ( !ser.check_pointer_pack(ptr) ) { serialize_impl<T*>()(t, ser); }
194  break;
195  case serializer::PACK:
196  // Always put the pointer in
197  ser.pack(ptr);
198 
199  // Nothing else to do if this is nullptr
200  if ( 0 == ptr ) return;
201 
202  if ( !ser.check_pointer_pack(ptr) ) { serialize_impl<T*>()(t, ser); }
203  break;
204  case serializer::UNPACK:
205  // Get the ptr and check to see if we've already deserialized
206  uintptr_t ptr_stored;
207  ser.unpack(ptr_stored);
208 
209  // Check to see if this was a nullptr
210  if ( 0 == ptr_stored ) {
211  t = nullptr;
212  return;
213  }
214 
215  uintptr_t real_ptr = ser.check_pointer_unpack(ptr_stored);
216  if ( real_ptr ) {
217  // Already deserialized, so just return pointer
218  t = reinterpret_cast<T*>(real_ptr);
219  }
220  else {
221  serialize_impl<T*>()(t, ser);
222  ser.report_real_pointer(ptr_stored, reinterpret_cast<uintptr_t>(t));
223  }
224  }
225  }
226 };
227 
228 /**
229  Version of serialize that works for fundamental types and enums.
230  */
231 template <class T>
232 class serialize_impl<T, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value>::type>
233 {
234  template <class A>
235  friend class serialize;
236  inline void operator()(T& t, serializer& ser) { ser.primitive(t); }
237 };
238 
239 /**
240  Version of serialize that works for bool.
241  */
242 template <>
243 class serialize_impl<bool>
244 {
245  template <class A>
246  friend class serialize;
247  void operator()(bool& t, serializer& ser)
248  {
249  int bval = t;
250  ser.primitive(bval);
251  t = bool(bval);
252  }
253 };
254 
255 /**
256  Version of serialize that works for pointers to fundamental types
257  and enums. Note that the pointer tracking happens at a higher
258  level, and only if it is turned on. If it is not turned on, then
259  this only copies the value pointed to into the buffer. If multiple
260  objects point to the same location, they will each have an
261  independent copy after deserialization.
262  */
263 template <class T>
264 class serialize_impl<T*, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value>::type>
265 {
266  template <class A>
267  friend class serialize;
268  inline void operator()(T*& t, serializer& ser)
269  {
270  switch ( ser.mode() ) {
271  case serializer::SIZER:
272  ser.size(*t);
273  break;
274  case serializer::PACK:
275  ser.primitive(*t);
276  break;
277  case serializer::UNPACK:
278  t = new T();
279  ser.primitive(*t);
280  break;
281  }
282  }
283 };
284 
285 
286 /**
287  Version of serialize that works for std::pair.
288  */
289 template <class U, class V>
290 class serialize_impl<std::pair<U, V>>
291 {
292  template <class A>
293  friend class serialize;
294  inline void operator()(std::pair<U, V>& t, serializer& ser)
295  {
296  serialize<U>()(t.first, ser);
297  serialize<V>()(t.second, ser);
298  }
299 };
300 
301 // /**
302 // Version of serialize that works for non-polymorphic types that have
303 // a serialize_order function
304 // */
305 // template <class T>
306 // class serialize_impl<T, typename std::enable_if<std::is_class<T>::value && !std::is_polymorphic<T>::value>::type>
307 // {
308 // template <class A>
309 // friend class serialize;
310 // inline void operator()(T& t, serializer& ser)
311 // {
312 // t.serialize_order(ser);
313 // }
314 // };
315 
316 // All calls to serialize objects need to go through this function so
317 // that pointer tracking can be done
318 template <class T>
319 inline void
320 operator&(serializer& ser, T& t)
321 {
322  serialize<T>()(t, ser);
323 }
324 template <class T>
325 
326 inline void
327 operator|(serializer& ser, T& t)
328 {
329  serialize<T>().serialize_and_track_pointer(t, ser);
330 }
331 } // namespace Serialization
332 } // namespace Core
333 } // namespace SST
334 
335 #include "sst/core/serialization/serialize_array.h"
336 #include "sst/core/serialization/serialize_atomic.h"
337 #include "sst/core/serialization/serialize_deque.h"
338 #include "sst/core/serialization/serialize_list.h"
339 #include "sst/core/serialization/serialize_map.h"
340 #include "sst/core/serialization/serialize_priority_queue.h"
341 #include "sst/core/serialization/serialize_set.h"
342 #include "sst/core/serialization/serialize_string.h"
343 #include "sst/core/serialization/serialize_vector.h"
344 
345 #endif // SST_CORE_SERIALIZATION_SERIALIZE_H
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:35
Base serialize class.
Definition: serialize.h:32
Definition: action.cc:18
Serialization "gateway" object.
Definition: serialize.h:110
void serialize_and_track_pointer(T &t, serializer &ser)
This will track the pointer to the object if pointer tracking is turned on.
Definition: serialize.h:123