SST 16.1.0
Structural Simulation Toolkit
serialize.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_SERIALIZE_H
13#define SST_CORE_SERIALIZATION_SERIALIZE_H
14
15#include "sst/core/from_string.h"
16#include "sst/core/output.h"
17#include "sst/core/serialization/objectMap.h"
18#include "sst/core/serialization/serializer.h"
19#include "sst/core/warnmacros.h"
20
21#define SST_INCLUDING_SERIALIZE_H
22#include "sst/core/serialization/impl/serialize_utility.h"
23#undef SST_INCLUDING_SERIALIZE_H
24
25#include <atomic>
26#include <cstdint>
27#include <iostream>
28#include <type_traits>
29#include <typeinfo>
30
31/**
32 Macro used in serialize_impl<> template specializations to
33 correctly friend the serialize<> template. The operator() function
34 should be private in the serialize_impl<> implementations.
35
36 NOTE: requires a semicolon after the call
37 */
38#define SST_FRIEND_SERIALIZE() \
39 template <class SER> \
40 friend class pvt::serialize
41
42namespace SST {
43
44/**
45 Types for options passed to serialization. Putting in a higher
46 namespace to ease use by shortening the fully qualified names
47**/
48
49/**
50 Type used to pass serialization options
51*/
52using ser_opt_t = uint32_t;
53
55{
56 /**
57 Options use to control how serialization acts for specific
58 elements that are serialized
59 */
60 enum Options : ser_opt_t {
61 // No set options
62 none = 0,
63 // Used to track the address of a non-pointer object when pointer
64 // tracking is on
65 as_ptr = 1u << 1,
66 // Used to pass the as_ptr option to the contents of a container
67 // when serialized
68 as_ptr_elem = 1u << 2,
69 // Used to specify a variable should be read-only in mapping mode
70 map_read_only = 1u << 3,
71 // Used to specify a variable should not be available in
72 // mapping mode
73 no_map = 1u << 4,
74 // User defined options are not currently supported
75 };
76
77 // Function to test if an options for serialization is
78 // set. Function marked constexpr so that it can evaluate at
79 // compile time if all values are available. Return type must
80 // match SST::Core::Serialization::ser_opt_t
81 static constexpr bool is_set(ser_opt_t flags, SerOption::Options option)
82 {
83 return flags & static_cast<ser_opt_t>(option);
84 }
85};
86
87namespace Core::Serialization {
88
89namespace pvt {
90
91template <typename T>
92void sst_ser_object(serializer& ser, T&& obj, ser_opt_t options, const char* name);
93
94// Proxy struct which represents a bit reference wrapper similar to std::reference_wrapper.
95// This proxy is needed in order for us to partially specialize serialize_impl for the
96// std::bitset<N>::reference and std::vector<bool>::reference types in mapping mode.
97template <typename T>
98struct bit_reference_wrapper
99{
100 typename T::reference ref;
101 explicit bit_reference_wrapper(typename T::reference ref) :
102 ref(ref)
103 {}
104 bit_reference_wrapper(const bit_reference_wrapper&) = default;
105 bit_reference_wrapper& operator=(const bit_reference_wrapper&) = delete;
106 ~bit_reference_wrapper() = default;
107};
108
109} // namespace pvt
110
111// get_ptr() returns reference to argument if it's a pointer, else address of argument
112template <typename T>
113constexpr decltype(auto)
114get_ptr(T& t)
115{
116 if constexpr ( std::is_pointer_v<T> )
117 return t;
118 else
119 return &t;
120}
121
122/**
123 Base serialize class.
124
125 This class also acts as the default case, which if hit will check
126 to see if this is an otherwise unhandled non-polymorphic class. If
127 it is, it will just attempt to call the serialize_order() function.
128 All other instances are partial specializations of this class and
129 do all the real serialization.
130 */
131template <typename T, typename = void>
133{
134 static_assert(std::is_class_v<std::remove_pointer_t<T>>,
135 "Trying to serialize a non-class type (or a pointer to one) with the default serialize_impl.");
136
137 static_assert(std::is_final_v<std::remove_pointer_t<T>> || !std::is_polymorphic_v<std::remove_pointer_t<T>>,
138 "The type or the pointed-to-type is polymorphic and non-final, so serialize_impl cannot know the runtime type "
139 "of the object being serialized. Polymorphic classes need to inherit from the serializable class and call the "
140 "ImplementSerializable() macro. For classes marked final, this is not necessary.");
141
142 static_assert(has_serialize_order_v<std::remove_pointer_t<T>>,
143 "The type or the pointed-to-type does not have a serialize_impl specialization, nor a public "
144 "serialize_order() method. This means that there is not a serialize_impl class specialization for "
145 "the type or a pointer to the type, and so the default serialize_impl is used which requires a "
146 "public serialize_order() method to be defined inside of the class type.");
147
148public:
149 void operator()(T& t, serializer& ser, ser_opt_t UNUSED(options))
150 {
151 const auto& tPtr = get_ptr(t);
152 const auto mode = ser.mode();
153 if ( mode == serializer::MAP ) {
154 if ( !tPtr ) return; // No need to map a nullptr
155 ObjectMap* map = new ObjectMapClass(tPtr, typeid(T).name());
156 ser.mapper().report_object_map(map);
157 ser.mapper().map_hierarchy_start(ser.getMapName(), map);
158 }
159 else if constexpr ( std::is_pointer_v<T> ) {
160 if ( mode == serializer::UNPACK ) {
161 t = new std::remove_pointer_t<T>();
162 ser.unpacker().report_new_pointer(reinterpret_cast<uintptr_t>(t));
163 }
164 }
165
166 // class needs to define a serialize_order() method
167 tPtr->serialize_order(ser);
168
169 if ( mode == serializer::MAP ) ser.mapper().map_hierarchy_end();
170 }
171};
172
173namespace pvt {
174
175/**
176 Serialization "gateway" object called by sst_ser_object(). All
177 serializations must come through these template instances in order
178 for pointer tracking to be controlled at one point. The actual
179 serialization will happen in serialize_impl classes.
180 */
181template <class T>
183{
184 template <class U>
185 friend void sst_ser_object(serializer& ser, U&& obj, ser_opt_t options, const char* name);
186
187 void operator()(T& t, serializer& ser, ser_opt_t options) { return serialize_impl<T>()(t, ser, options); }
188
189 /**
190 This will track the pointer to the object if pointer tracking
191 is turned on. Otherwise, it will just serialize normally. This
192 is only useful if you have a non-pointer struct/class where
193 other pieces of code will have pointers to it (e.g. a set/map
194 contains the actual struct and other places point to the data's
195 location)
196 */
197 void serialize_and_track_pointer(T& t, serializer& ser, ser_opt_t options)
198 {
199 if ( !ser.is_pointer_tracking_enabled() ) return serialize_impl<T>()(t, ser, options);
200
201 // Get the pointer to the data that will be uesd for tracking.
202 uintptr_t ptr = reinterpret_cast<uintptr_t>(&t);
203
204 switch ( ser.mode() ) {
205 case serializer::SIZER:
206 // Check to see if the pointer has already been seen. If
207 // so, then we error since the non-pointer version of this
208 // data needs to be serialized before any of the pointers
209 // that point to it.
210 if ( ser.sizer().check_pointer_sizer(ptr) ) {
211 Output::getDefaultObject().output(
212 "WARNING: Serializaion of object of type %s using as_ptr option detected that a pointer to this "
213 "object was "
214 "already serialized. Serialization of the object should happen before serialization of any "
215 "pointers to the object.\n",
216 ObjectMap::demangle_name(typeid(T).name()).c_str());
217 }
218
219 // Always put the pointer in
220 ser.size(ptr);
221 // Count the data for the object
222 serialize_impl<T>()(t, ser, options);
223 break;
224 case serializer::PACK:
225 // Already checked serialization order during sizing, so
226 // don't need to do it here
227 ser.packer().check_pointer_pack(ptr);
228 // Always put the pointer in
229 ser.pack(ptr);
230 // Serialize the data for the object
231 serialize_impl<T>()(t, ser, options);
232 break;
233 case serializer::UNPACK:
234 // Checked order on serialization, so don't need to do it
235 // here
236
237 // Get the stored pointer to use as a tag for future
238 // references to the data
239 uintptr_t ptr_stored;
240 ser.unpack(ptr_stored);
241
242 // Now add this to the tracking data
243 ser.unpacker().report_real_pointer(ptr_stored, ptr);
244
245 serialize_impl<T>()(t, ser, options);
246 break;
247 case serializer::MAP:
248 serialize_impl<T>()(t, ser, options);
249 break;
250 }
251 }
252};
253
254template <class T>
255class serialize<T*>
256{
257 template <class U>
258 friend void sst_ser_object(serializer& ser, U&& obj, ser_opt_t options, const char* name);
259 void operator()(T*& t, serializer& ser, ser_opt_t options)
260 {
261 // We are a pointer, need to see if tracking is turned on
262 if ( !ser.is_pointer_tracking_enabled() ) {
263 switch ( ser.mode() ) {
264 case serializer::UNPACK:
265 t = nullptr; // Nullify the pointer in case it was null
266 [[fallthrough]];
267 default:
268 {
269 // We will always get/put a bool to tell whether or not this is nullptr.
270 bool nonnull = t != nullptr;
271 ser.primitive(nonnull);
272
273 // If not nullptr, serialize the object
274 if ( nonnull ) serialize_impl<T*>()(t, ser, options);
275 break;
276 }
277 case serializer::MAP:
278 break; // If this version of serialize gets called in mapping mode, there is nothing to do
279 }
280 return;
281 }
282
283 uintptr_t ptr = reinterpret_cast<uintptr_t>(t);
284 if ( nullptr == t ) ptr = 0;
285
286 switch ( ser.mode() ) {
287 case serializer::SIZER:
288 // Always put the pointer in
289 ser.size(ptr);
290
291 // If this is a nullptr, then we are done
292 if ( 0 == ptr ) return;
293
294 // If we haven't seen this yet, also need to serialize the
295 // object
296 if ( !ser.sizer().check_pointer_sizer(ptr) ) {
297 serialize_impl<T*>()(t, ser, options);
298 }
299 break;
300 case serializer::PACK:
301 // Always put the pointer in
302 ser.pack(ptr);
303
304 // Nothing else to do if this is nullptr
305 if ( 0 == ptr ) return;
306
307 if ( !ser.packer().check_pointer_pack(ptr) ) {
308 serialize_impl<T*>()(t, ser, options);
309 }
310 break;
311 case serializer::UNPACK:
312 {
313 // Get the ptr and check to see if we've already deserialized
314 uintptr_t ptr_stored;
315 ser.unpack(ptr_stored);
316
317 // Check to see if this was a nullptr
318 if ( 0 == ptr_stored ) {
319 t = nullptr;
320 return;
321 }
322
323 uintptr_t real_ptr = ser.unpacker().check_pointer_unpack(ptr_stored);
324 if ( real_ptr ) {
325 // Already deserialized, so just return pointer
326 t = reinterpret_cast<T*>(real_ptr);
327 }
328 else {
329 serialize_impl<T*>()(t, ser, options);
330 ser.unpacker().report_real_pointer(ptr_stored, reinterpret_cast<uintptr_t>(t));
331 }
332 break;
333 }
334 case serializer::MAP:
335 {
336 ObjectMap* map = ser.mapper().check_pointer_map(reinterpret_cast<uintptr_t>(t));
337 if ( map != nullptr ) {
338 // If we've already seen this object, just add the
339 // existing ObjectMap to the parent.
340 ser.mapper().map_existing_object(ser.getMapName(), map);
341 }
342 else {
343 serialize_impl<T*>()(t, ser, options);
344 }
345 break;
346 }
347 }
348 }
349};
350
351// All serialization must go through this function to ensure
352// everything works correctly
353//
354// A universal/forwarding reference is used for obj so that it can match rvalue wrappers like
355// SST::Core::Serialization::array(ary, size) but then it is used as an lvalue so that it
356// matches serialization functions which only take lvalue references.
357template <class TREF>
358void
359sst_ser_object(serializer& ser, TREF&& obj, ser_opt_t options, const char* name)
360{
361 // TREF is an lvalue reference when obj argument is an lvalue reference, so we remove the reference
362 using T = std::remove_reference_t<TREF>;
363
364 // We will check for the "fast" path (i.e. event serialization for synchronizations). We can detect this by
365 // seeing if pointer tracking is turned off, because it is turned on for both checkpointing and mapping mode.
366 if ( !ser.is_pointer_tracking_enabled() ) {
367 // Options are wiped out since none apply in this case
368 pvt::serialize<T>()(obj, ser, SerOption::none);
369 }
370 else if ( ser.mode() == serializer::MAP ) {
371 // Mapping mode
372 // Check to see if we are NOMAP
373 if ( !SerOption::is_set(options, SerOption::no_map) ) {
374 ObjectMapContext context(ser, name);
375 pvt::serialize<T>()(obj, ser, options);
376 }
377 }
378 else if constexpr ( !std::is_pointer_v<T> ) {
379 // as_ptr is only valid for non-pointers
380 if ( SerOption::is_set(options, SerOption::as_ptr) ) {
381 pvt::serialize<T>().serialize_and_track_pointer(obj, ser, options);
382 }
383 else {
384 pvt::serialize<T>()(obj, ser, options);
385 }
386 }
387 else {
388 // For pointer types, just call serialize
389 pvt::serialize<T>()(obj, ser, options);
390 }
391}
392
393} // namespace pvt
394
395
396// Serialization macros for checkpoint/debug serialization
397#define SST_SER(obj, ...) \
398 SST::Core::Serialization::pvt::sst_ser_object( \
399 ser, (obj), SST::Core::Serialization::pvt::sst_ser_or_helper(__VA_ARGS__), #obj)
400
401#define SST_SER_NAME(obj, name, ...) \
402 SST::Core::Serialization::pvt::sst_ser_object( \
403 ser, (obj), SST::Core::Serialization::pvt::sst_ser_or_helper(__VA_ARGS__), name)
404
405namespace pvt {
406template <typename... Args>
407constexpr ser_opt_t
408sst_ser_or_helper(Args... args)
409{
410 return (SerOption::none | ... | args); // Fold expression to perform logical OR
411}
412
413} // namespace pvt
414
415// Serialize an object and return an ObjectMap which represents it
416template <typename T>
418ObjectMapSerialization(T&& obj)
419{
420 ObjectMapClass root;
421 serializer ser;
422 ser.enable_pointer_tracking();
423 ser.start_mapping(&root);
424 SST_SER_NAME(obj, "_proxy_object_");
425 ObjectMap* ret = root.findVariable("_proxy_object_");
426 if ( ret ) ret->incRefCount();
427 return ret;
428}
429
430} // namespace Core::Serialization
431} // namespace SST
432
433// These includes have guards to print warnings if they are included independent of this file.
434// Set the #define that will disable the warnings.
435#define SST_INCLUDING_SERIALIZE_H
436#include "sst/core/serialization/impl/serialize_adapter.h"
437#include "sst/core/serialization/impl/serialize_aggregate.h"
438#include "sst/core/serialization/impl/serialize_array.h"
439#include "sst/core/serialization/impl/serialize_atomic.h"
440#include "sst/core/serialization/impl/serialize_bitset.h"
441#include "sst/core/serialization/impl/serialize_insertable.h"
442#include "sst/core/serialization/impl/serialize_optional.h"
443#include "sst/core/serialization/impl/serialize_shared_ptr.h"
444#include "sst/core/serialization/impl/serialize_string.h"
445#include "sst/core/serialization/impl/serialize_trivial.h"
446#include "sst/core/serialization/impl/serialize_tuple.h"
447#include "sst/core/serialization/impl/serialize_unique_ptr.h"
448#include "sst/core/serialization/impl/serialize_valarray.h"
449#include "sst/core/serialization/impl/serialize_variant.h"
450
451// Reenble warnings for including the above file independent of this file.
452#undef SST_INCLUDING_SERIALIZE_H
453
454#endif // SST_CORE_SERIALIZATION_SERIALIZE_H
ObjectMap object for non-fundamental, non-container types.
Definition objectMap.h:747
ObjectMapClass()=default
Default constructor.
ObjectMap context which is saved in a virtual stack when name or other information changes.
Definition serializer.h:158
Base class for objects created by the serializer mapping mode used to map the variables for objects.
Definition objectMap.h:189
void incRefCount()
Increment the reference counter for this ObjectMap.
Definition objectMap.h:338
ObjectMap * findVariable(const std::string &name, bool confirm=false) const
Find a variable in this object map.
Definition objectMap.cc:228
static std::string demangle_name(const char *name)
Static function to demangle type names returned from typeid(T).name().
Definition objectMap.cc:152
Serialization "gateway" object called by sst_ser_object().
Definition serialize.h:183
Base serialize class.
Definition serialize.h:133
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.h:55
Options
Options use to control how serialization acts for specific elements that are serialized.
Definition serialize.h:60