SST  14.1.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/from_string.h"
16 #include "sst/core/serialization/objectMap.h"
17 #include "sst/core/serialization/serializer.h"
18 #include "sst/core/warnmacros.h"
19 
20 #include <atomic>
21 #include <iostream>
22 #include <type_traits>
23 #include <typeinfo>
24 
25 namespace SST {
26 namespace Core {
27 namespace Serialization {
28 
29 // Workaround for use with static_assert(), since static_assert(false)
30 // will always assert, even when in an untaken if constexpr path.
31 // This can be used in any serialize_impl class, if needed/
32 template <class>
33 constexpr bool dependent_false = false;
34 
35 /**
36  Base serialize class.
37 
38  This class also acts as the default case, which if hit will check
39  to see if this is an otherwise uncaught non-polymorphic class. If
40  it is, it will just attempt to call the serialize_order() function.
41  All other instances are partial specializations of this class and
42  do all the real serialization.
43  */
44 template <class T, class Enable = void>
46 {
47 public:
48  inline void operator()(T& t, serializer& ser)
49  {
50  // This is the fall through case. Check to see if it's a pointer:
51  if constexpr ( std::is_pointer_v<T> ) {
52  // If it falls through to the default, let's check to see if it's
53  // a non-polymorphic class and try to call serialize_order
54  if constexpr (
55  std::is_class_v<typename std::remove_pointer<T>::type> &&
56  !std::is_polymorphic_v<typename std::remove_pointer<T>::type> ) {
57  if ( ser.mode() == serializer::UNPACK ) {
58  t = new typename std::remove_pointer<T>::type();
59  ser.report_new_pointer(reinterpret_cast<uintptr_t>(t));
60  }
61  t->serialize_order(ser);
62  }
63  else {
64  static_assert(dependent_false<T>, "Trying to serialize an object that is not serializable.");
65  }
66  }
67  else {
68  // If it falls through to the default, let's check to see if it's
69  // a non-polymorphic class and try to call serialize_order
70  if constexpr ( std::is_class_v<T> && !std::is_polymorphic_v<T> ) { t.serialize_order(ser); }
71  else {
72  static_assert(dependent_false<T>, "Trying to serialize an object that is not serializable.");
73  }
74  }
75  }
76 
77  inline void operator()(T& t, serializer& ser, const char* name)
78  {
79  // This is the fall through case. Check to see if it's a pointer:
80  if constexpr ( std::is_pointer_v<T> ) {
81  // If it falls through to the default, let's check to see if it's
82  // a non-polymorphic class and try to call serialize_order
83  if constexpr (
84  std::is_class_v<typename std::remove_pointer<T>::type> &&
85  !std::is_polymorphic_v<typename std::remove_pointer<T>::type> ) {
86  if ( ser.mode() == serializer::UNPACK ) {
87  t = new typename std::remove_pointer<T>::type();
88  ser.report_new_pointer(reinterpret_cast<uintptr_t>(t));
89  }
90  if ( ser.mode() == serializer::MAP ) {
91  // No need to map a nullptr
92  if ( nullptr == t ) return;
94  new SST::Core::Serialization::ObjectMapClass(t, typeid(T).name());
95  ser.report_object_map(map);
96  ser.mapper().map_hierarchy_start(name, map);
97  }
98  t->serialize_order(ser);
99  if ( ser.mode() == serializer::MAP ) ser.mapper().map_hierarchy_end();
100  }
101  else {
102  static_assert(dependent_false<T>, "Trying to serialize an object that is not serializable.");
103  }
104  }
105  else {
106  // If it falls through to the default, let's check to see if it's
107  // a non-polymorphic class and try to call serialize_order
108  if constexpr ( std::is_class_v<T> && !std::is_polymorphic_v<T> ) {
109  if ( ser.mode() == serializer::MAP ) {
111  new SST::Core::Serialization::ObjectMapClass(&t, typeid(T).name());
112  ser.report_object_map(map);
113  ser.mapper().map_hierarchy_start(name, map);
114  }
115  t.serialize_order(ser);
116  if ( ser.mode() == serializer::MAP ) ser.mapper().map_hierarchy_end();
117  }
118  else {
119  static_assert(dependent_false<T>, "Trying to serialize an object that is not serializable.");
120  }
121  }
122  }
123 };
124 
125 
126 /**
127  Serialization "gateway" object. All serializations must come
128  thorugh these template instances in order for pointer tracking to
129  be controlled at one point. The actual serialization will happen in
130  serialize_impl classes.
131  */
132 template <class T>
134 {
135 public:
136  inline void operator()(T& t, serializer& ser) { return serialize_impl<T>()(t, ser); }
137  inline void operator()(T& t, serializer& ser, const char* name) { return serialize_impl<T>()(t, ser, name); }
138 
139  /**
140  This will track the pointer to the object if pointer tracking
141  is turned on. Otherwise, it will just serialize normally. This
142  is only useful if you have a non-pointer struct/class where
143  other pieces of code will have pointers to it (e.g. a set/map
144  contains the actual struct and other places point to the data's
145  location)
146  */
147  inline void serialize_and_track_pointer(T& t, serializer& ser)
148  {
149  if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T>()(t, ser);
150 
151  // Get the pointer to the data that will be uesd for tracking.
152  uintptr_t ptr = reinterpret_cast<uintptr_t>(&t);
153 
154  switch ( ser.mode() ) {
155  case serializer::SIZER:
156  // Check to see if the pointer has already been seen. If
157  // so, then we error since the non-pointer version of this
158  // data needs to be serialized before any of the pointers
159  // that point to it.
160  if ( ser.check_pointer_pack(ptr) ) {
161  // Error
162  }
163 
164  // Always put the pointer in
165  ser.size(ptr);
166  // Count the data for the object
167  serialize_impl<T>()(t, ser);
168  break;
169  case serializer::PACK:
170  // Already checked serialization order during sizing, so
171  // don't need to do it here
172  ser.check_pointer_pack(ptr);
173  // Always put the pointer in
174  ser.pack(ptr);
175  // Serialize the data for the object
176  serialize_impl<T>()(t, ser);
177  break;
178  case serializer::UNPACK:
179  // Checked order on serialization, so don't need to do it
180  // here
181 
182  // Get the stored pointer to use as a tag for future
183  // references to the data
184  uintptr_t ptr_stored;
185  ser.unpack(ptr_stored);
186 
187  // Now add this to the tracking data
188  ser.report_real_pointer(ptr_stored, ptr);
189 
190  serialize_impl<T>()(t, ser);
191  case serializer::MAP:
192  // Add your code here
193  break;
194  }
195  }
196 };
197 
198 template <class T>
199 class serialize<T*>
200 {
201 public:
202  inline void operator()(T*& t, serializer& ser)
203  {
204  // We are a pointer, need to see if tracking is turned on
205  if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T*>()(t, ser);
206 
207  uintptr_t ptr = reinterpret_cast<uintptr_t>(t);
208  if ( nullptr == t ) ptr = 0;
209 
210  switch ( ser.mode() ) {
211  case serializer::SIZER:
212  // Always put the pointer in
213  ser.size(ptr);
214 
215  // If this is a nullptr, then we are done
216  if ( 0 == ptr ) return;
217 
218  // If we haven't seen this yet, also need to serialize the
219  // object
220  if ( !ser.check_pointer_pack(ptr) ) { serialize_impl<T*>()(t, ser); }
221  break;
222  case serializer::PACK:
223  // Always put the pointer in
224  ser.pack(ptr);
225 
226  // Nothing else to do if this is nullptr
227  if ( 0 == ptr ) return;
228 
229  if ( !ser.check_pointer_pack(ptr) ) { serialize_impl<T*>()(t, ser); }
230  break;
231  case serializer::UNPACK:
232  {
233  // Get the ptr and check to see if we've already deserialized
234  uintptr_t ptr_stored;
235  ser.unpack(ptr_stored);
236 
237  // Check to see if this was a nullptr
238  if ( 0 == ptr_stored ) {
239  t = nullptr;
240  return;
241  }
242 
243  uintptr_t real_ptr = ser.check_pointer_unpack(ptr_stored);
244  if ( real_ptr ) {
245  // Already deserialized, so just return pointer
246  t = reinterpret_cast<T*>(real_ptr);
247  }
248  else {
249  serialize_impl<T*>()(t, ser);
250  ser.report_real_pointer(ptr_stored, reinterpret_cast<uintptr_t>(t));
251  }
252  }
253  case serializer::MAP:
254  // Add your code here
255  break;
256  }
257  }
258 
259  inline void operator()(T*& t, serializer& ser, const char* name)
260  {
261  // We are a pointer, need to see if tracking is turned on
262  // if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T*>()(t, ser);
263  // The name version of the function is only used in mapping
264  // mode. If it's not mapping mode, it's an error.
265  // TODO: Add error and exit
266  if ( ser.mode() != serializer::MAP ) return;
267 
268  ObjectMap* map = ser.check_pointer_map(reinterpret_cast<uintptr_t>(t));
269  if ( map != nullptr ) {
270  // If we've already seen this object, just add the
271  // existing ObjectMap to the parent.
272  ser.mapper().map_existing_object(name, map);
273  return;
274  }
275  serialize_impl<T*>()(t, ser, name);
276  }
277 };
278 
279 
280 /**
281  Version of serialize that works for fundamental types and enums.
282  */
283 
284 template <class T>
285 class serialize_impl<T, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value>::type>
286 {
287  template <class A>
288  friend class serialize;
289  inline void operator()(T& t, serializer& ser) { ser.primitive(t); }
290 
291  inline void operator()(T& t, serializer& ser, const char* name)
292  {
294  ser.mapper().map_primitive(name, obj_map);
295  }
296 };
297 
298 /**
299  Version of serialize that works for bool.
300  */
301 template <>
302 class serialize_impl<bool>
303 {
304  template <class A>
305  friend class serialize;
306  void operator()(bool& t, serializer& ser)
307  {
308  int bval = t;
309  ser.primitive(bval);
310  t = bool(bval);
311  }
312 
313  inline void operator()(bool& t, serializer& ser, const char* name)
314  {
316  ser.mapper().map_primitive(name, obj_map);
317  }
318 };
319 
320 /**
321  Version of serialize that works for pointers to fundamental types
322  and enums. Note that the pointer tracking happens at a higher
323  level, and only if it is turned on. If it is not turned on, then
324  this only copies the value pointed to into the buffer. If multiple
325  objects point to the same location, they will each have an
326  independent copy after deserialization.
327  */
328 template <class T>
329 class serialize_impl<T*, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value>::type>
330 {
331  template <class A>
332  friend class serialize;
333  inline void operator()(T*& t, serializer& ser)
334  {
335  switch ( ser.mode() ) {
336  case serializer::SIZER:
337  ser.size(*t);
338  break;
339  case serializer::PACK:
340  ser.primitive(*t);
341  break;
342  case serializer::UNPACK:
343  t = new T();
344  ser.primitive(*t);
345  break;
346  case serializer::MAP:
347  // Add your code here
348  break;
349  }
350  }
351 };
352 
353 
354 /**
355  Version of serialize that works for std::pair.
356  */
357 template <class U, class V>
358 class serialize_impl<std::pair<U, V>>
359 {
360  template <class A>
361  friend class serialize;
362  inline void operator()(std::pair<U, V>& t, serializer& ser)
363  {
364  serialize<U>()(t.first, ser);
365  serialize<V>()(t.second, ser);
366  }
367 };
368 
369 // /**
370 // Version of serialize that works for non-polymorphic types that have
371 // a serialize_order function
372 // */
373 // template <class T>
374 // class serialize_impl<T, typename std::enable_if<std::is_class<T>::value && !std::is_polymorphic<T>::value>::type>
375 // {
376 // template <class A>
377 // friend class serialize;
378 // inline void operator()(T& t, serializer& ser)
379 // {
380 // t.serialize_order(ser);
381 // }
382 // };
383 
384 // All calls to serialize objects need to go through one of the
385 // following functions, or through the serialize<T> template so that
386 // pointer tracking can be done.
387 template <class T>
388 inline void
389 operator&(serializer& ser, T& t)
390 {
391  serialize<T>()(t, ser);
392 }
393 template <class T>
394 
395 inline void
396 operator|(serializer& ser, T& t)
397 {
398  serialize<T>().serialize_and_track_pointer(t, ser);
399 }
400 
401 template <class T>
402 inline void
403 sst_map_object(serializer& ser, T& t, const char* name)
404 {
405  // This function is only used in mapping mode. If we're not in
406  // mapping mode, we will just call into the basic
407  // serialize<T>()(t,ser) path.
408  if ( ser.mode() == serializer::MAP ) { serialize<T>()(t, ser, name); }
409  else {
410  serialize<T>()(t, ser);
411  }
412 }
413 
414 // Serialization macros for checkpoint/debug serialization
415 // #define SST_SER(obj) ser& obj;
416 #define SST_SER(obj) sst_map_object(ser, obj, #obj);
417 #define SST_SER_AS_PTR(obj) ser | obj;
418 
419 } // namespace Serialization
420 } // namespace Core
421 } // namespace SST
422 
423 // These includes have guards to print warnings if they are included
424 // independent of this file. Set the #define that will disable the
425 // warnings.
426 #define SST_INCLUDING_SERIALIZE_H
427 #include "sst/core/serialization/impl/serialize_array.h"
428 #include "sst/core/serialization/impl/serialize_atomic.h"
429 #include "sst/core/serialization/impl/serialize_deque.h"
430 #include "sst/core/serialization/impl/serialize_list.h"
431 #include "sst/core/serialization/impl/serialize_map.h"
432 #include "sst/core/serialization/impl/serialize_priority_queue.h"
433 #include "sst/core/serialization/impl/serialize_set.h"
434 #include "sst/core/serialization/impl/serialize_string.h"
435 #include "sst/core/serialization/impl/serialize_vector.h"
436 // Reenble warnings for including the above file independent of this
437 // file.
438 #undef SST_INCLUDING_SERIALIZE_H
439 
440 #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:43
ObjectMap object fundamental types, and classes treated as fundamental types.
Definition: objectMap.h:501
Base serialize class.
Definition: serialize.h:45
Definition: action.cc:18
Class created by the serializer mapping mode used to map the variables for objects.
Definition: objectMap.h:61
ObjectMap object for non-fundamental, non-container types.
Definition: objectMap.h:453
Serialization "gateway" object.
Definition: serialize.h:133
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:147