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