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