SST  15.1.0
StructuralSimulationToolkit
serialize_unique_ptr.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_UNIQUE_PTR_H
13 #define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_UNIQUE_PTR_H
14 
15 #ifndef SST_INCLUDING_SERIALIZE_H
16 #warning \
17  "The header file sst/core/serialization/impl/serialize_unique_ptr.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 <memory>
24 #include <new>
25 #include <type_traits>
26 #include <utility>
27 
28 namespace SST::Core::Serialization {
29 
30 namespace pvt {
31 
32 // std::unique_ptr wrapper class
33 // Default template has a reference to a std::unique_ptr and a deleter which might be defaulted
34 template <class PTR_TYPE, class DELETER = std::default_delete<PTR_TYPE>, class SIZE_T = void>
36 {
37  std::unique_ptr<PTR_TYPE, DELETER>& ptr;
38  DELETER del {};
39 };
40 
41 // For unbounded array types, a reference to a size parameter is added
42 template <class ELEM_TYPE, class DELETER, class SIZE_T>
43 struct unique_ptr_wrapper<ELEM_TYPE[], DELETER, SIZE_T>
44 {
45  std::unique_ptr<ELEM_TYPE[], DELETER>& ptr;
46  SIZE_T& size;
47  DELETER del {};
48 };
49 
50 } // namespace pvt
51 
52 // Serialize std::unique_ptr with a default deleter but not an unbounded array type
53 template <class PTR_TYPE>
54 class serialize_impl<std::unique_ptr<PTR_TYPE>, std::enable_if_t<!is_unbounded_array_v<PTR_TYPE>>>
55 {
56  void operator()(std::unique_ptr<PTR_TYPE>& ptr, serializer& ser, ser_opt_t opt)
57  {
58  // Create a wrapper with a default deleter and serialize the wrapper with the generalized code below
59  pvt::unique_ptr_wrapper<PTR_TYPE> wrapper { ptr };
61  }
62  SST_FRIEND_SERIALIZE();
63 };
64 
65 // Serialize std::unique_ptr with a wrapper which handles unbounded arrays with a runtime size, and custom deleters
66 template <class PTR_TYPE, class DELETER, class SIZE_T>
67 class serialize_impl<pvt::unique_ptr_wrapper<PTR_TYPE, DELETER, SIZE_T>>
68 {
69  // OWNER_TYPE is PTR_TYPE with cv-qualifiers removed so that it can be serialized
70  using OWNER_TYPE = std::remove_cv_t<PTR_TYPE>;
71 
72  // ELEM_TYPE is the array element type, or OWNER_TYPE if OWNER_TYPE is not an array
73  // "pointer" member of std::unique_ptr is used in case DELETER has its own pointer type
74  using ELEM_TYPE = std::remove_cv_t<std::remove_pointer_t<typename std::unique_ptr<PTR_TYPE, DELETER>::pointer>>;
75 
76  void operator()(pvt::unique_ptr_wrapper<PTR_TYPE, DELETER, SIZE_T>& ptr, serializer& ser, ser_opt_t opt)
77  {
78  const auto opts = SerOption::is_set(opt, SerOption::as_ptr_elem) ? SerOption::as_ptr : SerOption::none;
79  const auto mode = ser.mode();
80 
81  if ( mode == serializer::MAP ) {
82  // TODO: Mapping std::unique_ptr
83  return;
84  }
85 
86  // Destroy the old std::unique_ptr
87  if ( mode == serializer::UNPACK ) ptr.ptr.~unique_ptr();
88 
89  size_t size = 0;
90  if constexpr ( is_unbounded_array_v<PTR_TYPE> ) {
91  // If PTR_TYPE is an unbounded array and there is a size parameter
92 
93  // Get the array size, which is 0 if the pointer is null
94  if ( mode != serializer::UNPACK )
95  if ( ptr.ptr )
96  size = get_array_size(ptr.size,
97  "Serialization Error: Array size in SST::Core::Serialization::unique_ptr() cannot fit "
98  "inside size_t. size_t should be used for array sizes.\n");
99 
100  // Serialize the array size
101  ser.primitive(size);
102  if ( mode == serializer::UNPACK ) ptr.size = static_cast<SIZE_T>(size);
103  }
104 
105  // Serialize the address stored in the std::unique_ptr
106  uintptr_t ptr_stored = reinterpret_cast<uintptr_t>(ptr.ptr.get());
107  ser.primitive(ptr_stored);
108 
109  uintptr_t real_ptr = ptr_stored;
110  bool serialize_obj = false;
111 
112  switch ( mode ) {
113  case serializer::SIZER:
114  serialize_obj = ptr_stored && !ser.sizer().check_pointer_sizer(ptr_stored);
115  break;
116 
117  case serializer::PACK:
118  serialize_obj = ptr_stored && !ser.packer().check_pointer_pack(ptr_stored);
119  break;
120 
121  case serializer::UNPACK:
122  if ( ptr_stored ) {
123  // Check if the pointer was seen before
124  real_ptr = ser.unpacker().check_pointer_unpack(ptr_stored);
125 
126  // If pointer was not seen before, allocate the object, report its address and serialize the object
127  if ( !real_ptr ) {
128  if constexpr ( is_unbounded_array_v<PTR_TYPE> )
129  real_ptr = reinterpret_cast<uintptr_t>(new ELEM_TYPE[size]());
130  else if constexpr ( std::is_array_v<PTR_TYPE> )
131  real_ptr = reinterpret_cast<uintptr_t>(new ELEM_TYPE[std::extent_v<PTR_TYPE>]());
132  else
133  real_ptr = reinterpret_cast<uintptr_t>(new OWNER_TYPE());
134  ser.unpacker().report_real_pointer(ptr_stored, real_ptr);
135  serialize_obj = true;
136  }
137  }
138  // Create a std::unique_ptr owning the real pointer (or null pointer if ptr_stored == 0)
139  new (&ptr.ptr) std::unique_ptr<PTR_TYPE, DELETER>(
140  reinterpret_cast<ELEM_TYPE*>(real_ptr), std::forward<decltype(ptr.del)>(ptr.del));
141  break;
142 
143  default:
144  break;
145  }
146 
147  // Serialize the object if the pointer is non-null and this is the first time it's been seen
148  if ( serialize_obj ) {
149  if constexpr ( !is_unbounded_array_v<PTR_TYPE> )
150  SST_SER(*reinterpret_cast<OWNER_TYPE*>(real_ptr));
151  else if constexpr ( is_trivially_serializable_v<ELEM_TYPE> )
152  ser.raw(reinterpret_cast<ELEM_TYPE*>(real_ptr), size * sizeof(ELEM_TYPE));
153  else
154  pvt::serialize_array(
155  ser, reinterpret_cast<ELEM_TYPE*>(real_ptr), opts, size, pvt::serialize_array_element<ELEM_TYPE>);
156  }
157  }
158 
159  // For the serialize_impl specialization above
161 
162  SST_FRIEND_SERIALIZE();
163 };
164 
165 // Wrapper for a std::unique_ptr to an unbounded array with a runtime size
166 template <class ELEM_TYPE, class DELETER, class SIZE_T>
167 std::enable_if_t<std::is_integral_v<SIZE_T>, pvt::unique_ptr_wrapper<ELEM_TYPE[], DELETER, SIZE_T>>
168 unique_ptr(std::unique_ptr<ELEM_TYPE[], DELETER>& ptr, SIZE_T& size)
169 {
170  return { ptr, size };
171 }
172 
173 // Wrapper for a std::unique_ptr with a custom deleter but no unbounded array
174 template <class PTR_TYPE, class DELETER, class DEL>
175 std::enable_if_t<!is_unbounded_array_v<PTR_TYPE> && std::is_constructible_v<DELETER, DEL&&>,
176  pvt::unique_ptr_wrapper<PTR_TYPE, DELETER>>
177 unique_ptr(std::unique_ptr<PTR_TYPE, DELETER>& ptr, DEL&& del)
178 {
179  return { ptr, std::forward<DEL>(del) };
180 }
181 
182 // Wrapper for a std::unique_ptr with a custom deleter and an unbounded array with a runtime size
183 template <class ELEM_TYPE, class DELETER, class SIZE_T, class DEL>
184 std::enable_if_t<std::is_integral_v<SIZE_T> && std::is_constructible_v<DELETER, DEL&&>,
185  pvt::unique_ptr_wrapper<ELEM_TYPE[], DELETER, SIZE_T>>
186 unique_ptr(std::unique_ptr<ELEM_TYPE[], DELETER>& ptr, SIZE_T& size, DEL&& del)
187 {
188  return { ptr, size, std::forward<DEL>(del) };
189 }
190 
191 // NOP for consistency
192 template <class PTR_TYPE, class DELETER>
193 std::unique_ptr<PTR_TYPE, DELETER>&
194 unique_ptr(std::unique_ptr<PTR_TYPE, DELETER>& ptr)
195 {
196  return ptr;
197 }
198 
199 } // namespace SST::Core::Serialization
200 
201 #endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_UNIQUE_PTR_H
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:42
Base serialize class.
Definition: serialize.h:113
Definition: serialize_unique_ptr.h:35