SST  15.1.0
StructuralSimulationToolkit
serialize_utility.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_UTILITY_H
13 #define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_UTILITY_H
14 
15 #ifndef SST_INCLUDING_SERIALIZE_H
16 #warning \
17  "The header file sst/core/serialization/impl/serialize_utility.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 <bitset>
21 #include <cfloat>
22 #include <complex>
23 #include <cstddef>
24 #include <cstdint>
25 #include <type_traits>
26 #include <utility>
27 
28 namespace SST::Core::Serialization {
29 
30 /////////////////////////////////////////////////////////////////////////
31 // Whether two names are the same template. Similar to std::is_same_v. //
32 /////////////////////////////////////////////////////////////////////////
33 template <template <class...> class, template <class...> class>
34 constexpr bool is_same_template_v = false;
35 
36 template <template <class...> class T>
37 constexpr bool is_same_template_v<T, T> = true;
38 
39 template <template <class...> class T1, template <class...> class T2>
40 using is_same_template = std::bool_constant<is_same_template_v<T1, T2>>;
41 
42 ///////////////////////////////////////////////////////////////////////////////////////
43 // Whether a type is the same as a certain class template filled with type arguments //
44 ///////////////////////////////////////////////////////////////////////////////////////
45 template <class, template <class...> class>
46 constexpr bool is_same_type_template_v = false;
47 
48 template <template <class...> class T1, class... T1ARGS, template <class...> class T2>
49 constexpr bool is_same_type_template_v<T1<T1ARGS...>, T2> = is_same_template_v<T1, T2>;
50 
51 template <class T, template <class...> class TT>
52 using is_same_type_template = std::bool_constant<is_same_type_template_v<T, TT>>;
53 
54 ///////////////////////////////////////////////////////
55 // Pre-C++20 is_unbounded_array trait implementation //
56 ///////////////////////////////////////////////////////
57 #if __cplusplus < 202002l
58 
59 template <class T>
60 constexpr bool is_unbounded_array_v = false;
61 
62 template <class T>
63 constexpr bool is_unbounded_array_v<T[]> = true;
64 
65 template <class T>
66 using is_unbounded_array = std::bool_constant<is_unbounded_array_v<T>>;
67 
68 #else
69 
70 using std::is_unbounded_array;
71 using std::is_unbounded_array_v;
72 
73 #endif
74 
75 ///////////////////////////////////////////////////
76 // Whether a type has a serialize_order() method //
77 ///////////////////////////////////////////////////
78 
79 template <class, class = void>
80 struct has_serialize_order_impl : std::false_type
81 {};
82 
83 template <class T>
84 struct has_serialize_order_impl<T, decltype(std::declval<T>().serialize_order(std::declval<class serializer&>()))> :
85  std::true_type
86 {};
87 
88 // Workaround for GCC < 12 bug which does not handle access violations as SFINAE substitution failures
89 // If serializable_base is a base class of T, we assume that T has a public serialize_order() method.
90 // If serialize_order() is private or protected in a T derived from serializable_base, it will cause a
91 // compile-time error in serialize_impl when invoking T{}.serialize_order() even though it's true here.
92 template <class T>
93 using has_serialize_order = std::disjunction<std::is_base_of<class serializable_base, T>, has_serialize_order_impl<T>>;
94 
95 template <class T>
96 constexpr bool has_serialize_order_v = has_serialize_order<T>::value;
97 
98 //////////////////////////////////////////////////
99 // Compute the number of fields in an aggregate //
100 //////////////////////////////////////////////////
101 namespace pvt_nfields {
102 
103 // glob is convertible to any other type while initializing an aggregate
104 // The conversion function is only used in decltype() so it does not need to be defined
105 struct glob
106 {
107  template <class T>
108  operator T() const;
109 };
110 
111 // Whether N fields can be used to initialize an aggregate, i.e., number of aggregate fields >= N
112 // If the type is not an aggregate, always returns false, so that nfields<C> returns 0.
113 template <class C, class, bool = std::is_aggregate_v<C>, class = C>
114 constexpr bool nfields_ge_impl = false;
115 
116 // Try to initialize N fields in an aggregate, with glob() automatically converting to each field's
117 // type. If initialization fails, it is not an error (SFINAE) and this specialization won't apply.
118 template <class C, size_t... I>
119 constexpr bool nfields_ge_impl<C, std::index_sequence<I...>, true, decltype(C { (I, glob())... })> = true;
120 
121 // Whether the number of fields in an aggregate is greater than or equal to N
122 template <class C, size_t N>
123 constexpr bool nfields_ge = nfields_ge_impl<C, std::make_index_sequence<N>>;
124 
125 // Binary search in [L,H) for number of fields in aggregate. Stops at L when M == L.
126 template <class C, size_t L, size_t H, size_t M = L + (H - L) / 2, bool = M != L>
127 constexpr size_t b_search = L;
128 
129 // Next search is in [L,M)
130 template <class C, size_t L, size_t H, size_t M, bool>
131 constexpr size_t b_search_next = b_search<C, L, M>;
132 
133 // Next search is in [M,H)
134 template <class C, size_t L, size_t H, size_t M>
135 constexpr size_t b_search_next<C, L, H, M, true> = b_search<C, M, H>;
136 
137 // Choose [L,M) or [M,H) depending on whether nfields >= M
138 template <class C, size_t L, size_t H, size_t M>
139 constexpr size_t b_search<C, L, H, M, true> = b_search_next<C, L, H, M, nfields_ge<C, M>>;
140 
141 // Find an upper bound on the number of fields, doubling N as long as number of fields >= N
142 template <class C, size_t N = 1, bool = true>
143 constexpr size_t nfields = nfields<C, N * 2, nfields_ge<C, N * 2>>;
144 
145 // When the number of fields < N, perform binary search in [0,N)
146 template <class C, size_t N>
147 constexpr size_t nfields<C, N, false> = b_search<C, 0, N>;
148 
149 } // namespace pvt_nfields
150 
151 // The number of fields in an aggregate, initializable by { ... }. Returns 0 if the type is not aggregate.
152 template <class C>
153 constexpr size_t nfields = pvt_nfields::nfields<C>;
154 
155 /////////////////////////////////////////////////////////////////////////////////////////////////////
156 // Template metaprogramming to determine if a type is trivially serializable - that it can be read //
157 // and written as raw data without any special handling. //
158 // //
159 // It is one of these types: //
160 // - arithmetic (integral, floating-point) //
161 // - enumeration (including std::byte) //
162 // - member object pointer //
163 // - std::complex<T>, C99 _Complex //
164 // - std::bitset //
165 // - trivially copyable, standard layout aggregate with trivially serializable members and no //
166 // serialize_order() method //
167 // //
168 // An aggregate is a C-style array, std::array, or a class/struct/union with all-public non-static //
169 // data members and direct bases, no user-provided, inherited or explicit constructors (C++17), //
170 // no user-declared or inherited constructors (C++20), and no virtual functions or virtual bases. //
171 // //
172 // Pointers are not considered trivially serializable since they have specific addresses which //
173 // are not portable across runs, and may require special tracking and allocation. Pointers to //
174 // member functions may have ABI-specific pointers to data which are not portable across runs. //
175 // But pointers to member objects are typed offsets within a class, and are portable across runs. //
176 // //
177 // Note: If an aggregate is a union, only the first member will be considered active, and thus //
178 // cannot be a pointer. Rather than ban unions entirely, pointers in unions are strongly //
179 // discouraged and can cause unexpected results. //
180 // //
181 // Note: is_trivially_serializable<T> should return true if T is trivially serializable, even if //
182 // there exists a specialization for serialize_impl<T>. is_trivially_serialzable<T> is a trait //
183 // for trivial serializability in general, not a serialize_impl specialization test condition. //
184 // For example, it returns true for arrays of int, even if serialize_impl<int[N]> is specialized. //
185 // //
186 // For further reading on the meaning of "trivial": https://isocpp.org/files/papers/P3279R0.html //
187 /////////////////////////////////////////////////////////////////////////////////////////////////////
188 
189 namespace pvt_trivial {
190 
191 // If it's not a trivially copyable, standard layout aggregate without a serialize_order() method, it needs to
192 // be an integer, floating-point, enum or member object pointer, or one of the specializations listed later.
193 // std::conjunction_v and std::disjunction_v are used to short-circuit and speed up instead of using && and ||.
194 template <class C, bool = std::conjunction_v<std::is_aggregate<C>, std::is_trivially_copyable<C>,
195  std::is_standard_layout<C>, std::negation<has_serialize_order<C>>>>
196 constexpr bool is_trivially_serializable_v =
197  std::disjunction_v<std::is_arithmetic<C>, std::is_enum<C>, std::is_member_object_pointer<C>>;
198 
199 // glob_ts is convertible to any trivially serializable type
200 struct glob_ts
201 {
202  // The type of the conversion must be trivially serializable or the conversion fails
203  template <class C, class = std::enable_if_t<is_trivially_serializable_v<C>>>
204  operator C() const;
205 };
206 
207 // Whether all fields of an aggregate type are trivially serializable
208 template <class C, class, class = C>
209 constexpr bool has_ts_fields = false;
210 
211 // Try to initialize N fields in an aggregate, with glob_ts() automatically converting to each field's
212 // type. If conversion fails, it is not an error (SFINAE) and this specialization won't apply.
213 template <class C, size_t... I>
214 constexpr bool has_ts_fields<C, std::index_sequence<I...>, decltype(C { (I, glob_ts())... })> = true;
215 
216 // If it's a trivially copyable, standard layout aggregate, then all of its fields must be trivially serializable
217 template <class C>
218 constexpr bool is_trivially_serializable_v<C, true> = has_ts_fields<C, std::make_index_sequence<nfields<C>>>;
219 
220 // std::bitset is trivially serializable
221 template <size_t N>
222 constexpr bool is_trivially_serializable_v<std::bitset<N>, false> = true;
223 
224 // Complex numbers are trivially serializable
225 template <class C>
226 constexpr bool is_trivially_serializable_v<std::complex<C>, false> = true;
227 
228 #ifndef __STDC_NO_COMPLEX__
229 template <>
230 inline constexpr bool is_trivially_serializable_v<float _Complex, false> = true;
231 
232 template <>
233 inline constexpr bool is_trivially_serializable_v<double _Complex, false> = true;
234 
235 template <>
236 inline constexpr bool is_trivially_serializable_v<long double _Complex, false> = true;
237 #endif
238 
239 // Other floating-point types not covered by std::is_floating_point
240 #ifdef FLT16_MIN
241 template <>
242 inline constexpr bool is_trivially_serializable_v<_Float16, false> = true;
243 #endif
244 
245 // 128-bit integers
246 #ifdef __SIZEOF_INT128__
247 template <>
248 inline constexpr bool is_trivially_serializable_v<__int128, false> = true;
249 
250 template <>
251 inline constexpr bool is_trivially_serializable_v<unsigned __int128, false> = true;
252 #endif
253 
254 } // namespace pvt_trivial
255 
256 // Whether a type is trivially serializable
257 template <class C>
258 constexpr bool is_trivially_serializable_v = pvt_trivial::is_trivially_serializable_v<C>;
259 
260 template <class C>
261 using is_trivially_serializable = std::bool_constant<is_trivially_serializable_v<C>>;
262 
263 } // namespace SST::Core::Serialization
264 
265 #endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_UTILITY_H
Definition: serialize_utility.h:80
Definition: serialize_utility.h:105
Definition: serialize_utility.h:200