Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Base/PUPUtil.hpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.,
7 : : 2022-2024 J. Bakosi
8 : : All rights reserved. See the LICENSE file for details.
9 : : \brief Charm++ Pack/UnPack utilities
10 : : \details This file contains some extensions to Charm++'s Pack/UnPack
11 : : routines.
12 : : */
13 : : // *****************************************************************************
14 : : #ifndef PUPUtil_h
15 : : #define PUPUtil_h
16 : :
17 : : #include <type_traits>
18 : :
19 : : #include <unordered_map>
20 : : #include <unordered_set>
21 : : #include <variant>
22 : : #include <optional>
23 : :
24 : : #include "NoWarning/pup_stl.hpp"
25 : :
26 : : //! Extensions to Charm++'s Pack/Unpack routines
27 : : namespace PUP {
28 : :
29 : : //////////////////// Serialize enum class ////////////////////
30 : :
31 : : #if defined(__clang__)
32 : : #pragma clang diagnostic push
33 : : #pragma clang diagnostic ignored "-Wuninitialized"
34 : : #elif defined(STRICT_GNUC)
35 : : #pragma GCC diagnostic push
36 : : #pragma GCC diagnostic ignored "-Wuninitialized"
37 : : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
38 : : #endif
39 : :
40 : : //! \brief Pack/Unpack enum class.
41 : : //! \details In Charm++ usually both the pup() overload and an overload for
42 : : //! operator| are defined for all serializable types. However, we cannot
43 : : //! define operator| for enum class as it would conflict with Charm++'s
44 : : //! catch-all, template< class T > inline void operator|(PUP::er &p,T &t)
45 : : //! {...}.
46 : : //! \param[in] p Charm++'s pack/unpack object
47 : : //! \param[in] e Enum class to pack/unpack
48 : : template< typename E,
49 : : typename std::enable_if< std::is_enum< E >::value, int >::type = 0 >
50 : 18 : inline void pup( PUP::er& p, E& e ) {
51 : 18 : auto v = static_cast< typename std::underlying_type_t< E > >( e );
52 [ + - ]: 18 : p | v;
53 : 18 : e = static_cast< E >( v );
54 : 18 : }
55 : :
56 : : #if defined(__clang__)
57 : : #pragma clang diagnostic pop
58 : : #elif defined(STRICT_GNUC)
59 : : #pragma GCC diagnostic pop
60 : : #endif
61 : :
62 : : //////////////////// Serialize std::unordered_map ////////////////////
63 : :
64 : : //! Pack/Unpack std::unordered_map.
65 : : //! \param[in] p Charm++'s pack/unpack object
66 : : //! \param[in] m std::unordered_map< Key, T, Hash, KeyEqual > to pack/unpack
67 : : template< class Key,
68 : : class T,
69 : : class Hash = std::hash< Key >,
70 : : class KeyEqual = std::equal_to< Key > >
71 : 59362 : inline void pup( PUP::er& p, std::unordered_map< Key, T, Hash, KeyEqual >& m ) {
72 : 59362 : auto size = PUP_stl_container_size( p, m );
73 [ + + ]: 59362 : if (p.isUnpacking()) {
74 [ + + ]: 211487 : for (decltype(size) s=0; s<size; ++s) {
75 : 192601 : std::pair< Key, T > node;
76 [ + - ]: 192601 : p | node;
77 [ + - ]: 192601 : m.emplace( node );
78 : : }
79 : : } else {
80 [ + + ]: 522419 : for (auto& t : m) {
81 [ - - ]: 481943 : std::pair< Key, T > node( t );
82 [ + - ]: 481943 : p | node;
83 : : }
84 : : }
85 : 59362 : }
86 : : //! Pack/Unpack std::unordered_map.
87 : : //! \param[in] p Charm++'s pack/unpack object
88 : : //! \param[in] m std::unordered_map< Key, T, Hash, KeyEqual > to pack/unpack
89 : : template< class Key,
90 : : class T,
91 : : class Hash = std::hash< Key >,
92 : : class KeyEqual = std::equal_to< Key > >
93 : 59362 : inline void operator|( PUP::er& p,
94 : : std::unordered_map< Key, T, Hash, KeyEqual >& m )
95 : 59362 : { pup( p, m ); }
96 : :
97 : : //////////////////// Serialize std::unordered_set ////////////////////
98 : :
99 : : //! Pack/Unpack std::unordered_set.
100 : : //! \param[in] p Charm++'s pack/unpack object
101 : : //! \param[in] s std::unordered_set< Key, Hash, KeyEqual > to pack/unpack
102 : : template< class Key,
103 : : class Hash = std::hash< Key >,
104 : : class KeyEqual = std::equal_to< Key > >
105 : 14998 : inline void pup( PUP::er& p, std::unordered_set< Key, Hash, KeyEqual >& s ) {
106 : 14998 : auto size = PUP_stl_container_size( p, s );
107 [ + + ]: 14998 : if (p.isUnpacking()) {
108 [ + + ]: 48761 : for (decltype(size) i=0; i<size; ++i) {
109 : : Key node;
110 [ + - ]: 43989 : p | node;
111 [ + - ]: 43989 : s.emplace( node );
112 : : }
113 : : } else {
114 [ + + ]: 98204 : for (auto& t : s) {
115 : 87978 : Key node( t );
116 [ + - ]: 87978 : p | node;
117 : : }
118 : : }
119 : 14998 : }
120 : : //! Pack/Unpack std::unordered_set.
121 : : //! \param[in] p Charm++'s pack/unpack object
122 : : //! \param[in] s std::unordered_set< Key, Hash, KeyEqual > to pack/unpack
123 : : template< class Key,
124 : : class Hash = std::hash< Key >,
125 : : class KeyEqual = std::equal_to< Key > >
126 : 14998 : inline void operator|( PUP::er& p,
127 : : std::unordered_set< Key, Hash, KeyEqual >& s )
128 : 14998 : { pup( p, s ); }
129 : :
130 : : //////////////////// Serialize std::optional ////////////////////
131 : :
132 : : //! Pack/Unpack std::optional
133 : : //! \param[in] p Charm++'s pack/unpack object
134 : : //! \param[in] o std::optional< T > of arbitrary type T to pack/unpack
135 : : template< class T >
136 : 6 : inline void pup( PUP::er& p, std::optional< T >& o ) {
137 [ + + ][ + - ]: 6 : T underlying_value = o ? *o : T();
138 : 6 : bool exist = o ? true : false;
139 [ + - ]: 6 : p | exist;
140 [ + - ]: 6 : p | underlying_value;
141 [ + + ][ + - ]: 6 : o = exist ? std::make_optional(underlying_value) : std::nullopt;
142 : 6 : }
143 : : //! Pack/Unpack std::optional
144 : : //! \param[in] p Charm++'s pack/unpack object
145 : : //! \param[in] o std::optional< T > of arbitrary type T to pack/unpack
146 : : template< class T >
147 : 6 : inline void operator|( PUP::er& p, std::optional< T >& o ) { pup( p, o ); }
148 : :
149 : : //////////////////// Serialize std::variant ////////////////////
150 : :
151 : : // Since std::variant when default-constructed is initialized to hold a value of
152 : : // the first alternative of its type list, calling PUP that works based on a
153 : : // std::visit with a templated operator() would always incorrectly trigger the
154 : : // overload for the first type. Thus when PUPing a variant not only its value
155 : : // but its type must also be sent during migration. The pup operator template
156 : : // below achieves this by reading out not only the value but also its zero-based
157 : : // index of the type alternative that is currently held by the variant passed to
158 : : // its initializer constructor. The index and the variant are then PUPed and
159 : : // when unpacking, as an additional step, the variant is reconstructed using the
160 : : // index and the value in the variant. This latter is done by invoking an
161 : : // expansion of an initializer list, guaranteed to happen in order, stepping
162 : : // through the typelist in the variant. Thanks to Nils Deppe for simplifying
163 : : // the original version of this operation. See UnitTest/tests/Base/TestPUPUtil.h
164 : : // or Inciter::SchemeBase.h for puping a variant in action.
165 : :
166 : : //! Pack/Unpack helper for std::variant
167 : : //! \param[in,out] index Counter (location) for type in variant
168 : : //! \param[in] send_index Target counter (location) for type in variant
169 : : //! \param[in] p Charm++'s pack/unpack object
170 : : //! \param[in] var std::variant< Ts... > of arbitrary types to pack/unpack
171 : : template <class T, class... Ts>
172 : 12 : char pup_helper( std::size_t& index,
173 : : const std::size_t send_index,
174 : : PUP::er& p,
175 : : std::variant<Ts...>& var )
176 : : {
177 [ + + ]: 12 : if (index == send_index) {
178 [ + + ]: 6 : if (p.isUnpacking()) {
179 : 2 : T t{};
180 [ + - ]: 2 : p | t;
181 : 2 : var = std::move(t);
182 : : } else {
183 : 4 : p | std::get<T>(var);
184 : : }
185 : : }
186 : 12 : index++;
187 : 12 : return '0';
188 : : }
189 : :
190 : : //! Pack/Unpack std::variant
191 : : //! \param[in] p Charm++'s pack/unpack object
192 : : //! \param[in] var std::variant< Ts... > of arbitrary types to pack/unpack
193 : : template <class... Ts>
194 : 6 : void pup(PUP::er& p, std::variant<Ts...>& var) {
195 : 6 : std::size_t index = 0;
196 : 6 : auto send_index = var.index();
197 [ + - ]: 6 : p | send_index;
198 : 0 : (void)std::initializer_list<char>{
199 [ + - ][ + - ]: 6 : pup_helper<Ts>(index, send_index, p, var)...};
200 : 6 : }
201 : :
202 : : //! Pack/Unpack std::variant
203 : : //! \param[in] p Charm++'s pack/unpack object
204 : : //! \param[in] d std::variant< Ts... > of arbitrary types to pack/unpack
205 : : template <typename... Ts>
206 : 6 : inline void operator|(PUP::er& p, std::variant<Ts...>& d) {
207 : 6 : pup(p, d);
208 : 6 : }
209 : :
210 : : } // PUP::
211 : :
212 : : #endif // PUPUtil_h
|