@@ -76,6 +76,14 @@ namespace mpi {
76
76
D (unsigned long long , MPI_UNSIGNED_LONG_LONG);
77
77
#undef D
78
78
79
+ /* *
80
+ * @brief Specialization of mpi::mpi_type for enum types.
81
+ * @tparam E C++ enum type.
82
+ */
83
+ template <typename E>
84
+ requires (std::is_enum_v<E>)
85
+ struct mpi_type<E> : mpi_type<std::underlying_type_t<E>> {};
86
+
79
87
/* *
80
88
* @brief Specialization of mpi::mpi_type for `const` types.
81
89
* @tparam T C++ type.
@@ -94,6 +102,28 @@ namespace mpi {
94
102
*/
95
103
template <typename T> constexpr bool has_mpi_type<T, std::void_t <decltype(mpi_type<T>::get())>> = true ;
96
104
105
+ namespace detail {
106
+
107
+ // Helper struct to check if member types are mpi-serializable, i.e. have an associated mpi_type
108
+ struct serialize_checker {
109
+ template <typename T>
110
+ void operator &(T &)
111
+ requires (has_mpi_type<T>)
112
+ {}
113
+ };
114
+
115
+ } // namespace detail
116
+
117
+ /* *
118
+ * @brief A concept that checks if objects of a type can be serialized and deserialized.
119
+ * @tparam T Type to check.
120
+ */
121
+ template <typename T>
122
+ concept Serializable = requires(const T ac, T a, detail::serialize_checker ar) {
123
+ { ac.serialize (ar) } -> std::same_as<void >;
124
+ { a.deserialize (ar) } -> std::same_as<void >;
125
+ };
126
+
97
127
/* *
98
128
* @brief Create a new `MPI_Datatype` from a tuple.
99
129
*
@@ -135,8 +165,11 @@ namespace mpi {
135
165
* @brief Specialization of mpi::mpi_type for std::tuple.
136
166
* @tparam Ts Tuple element types.
137
167
*/
138
- template <typename ... T> struct mpi_type <std::tuple<T...>> {
139
- [[nodiscard]] static MPI_Datatype get () noexcept { return get_mpi_type (std::tuple<T...>{}); }
168
+ template <typename ... Ts> struct mpi_type <std::tuple<Ts...>> {
169
+ [[nodiscard]] static MPI_Datatype get () noexcept {
170
+ static MPI_Datatype type = get_mpi_type (std::tuple<Ts...>{});
171
+ return type;
172
+ }
140
173
};
141
174
142
175
/* *
@@ -156,15 +189,81 @@ namespace mpi {
156
189
* auto tie_data(foo f) {
157
190
* return std::tie(f.x, f.y);
158
191
* }
192
+ * @endcode
193
+ *
194
+ * @tparam U Type to be converted to an `MPI_Datatype`.
195
+ */
196
+ template <typename U>
197
+ requires (not Serializable<U>) and requires (U u) { tie_data (u); }
198
+ struct mpi_type <U> {
199
+ [[nodiscard]] static MPI_Datatype get () noexcept {
200
+ static MPI_Datatype type = get_mpi_type (tie_data (U{}));
201
+ return type;
202
+ }
203
+ };
204
+
205
+ namespace detail {
206
+
207
+ // Archive helper class to obtain MPI custom type info using references to class members.
208
+ struct mpi_archive {
209
+ std::vector<int > block_lengths{};
210
+ std::vector<MPI_Aint> displacements{};
211
+ std::vector<MPI_Datatype> types{};
212
+ MPI_Aint base_address{};
213
+
214
+ // Constructor sets the base address of the object.
215
+ explicit mpi_archive (const void *base) { MPI_Get_address (base, &base_address); }
216
+
217
+ // Overloaded operator& to process members to set the block lengths, displacements and MPI types.
218
+ template <typename T>
219
+ requires (has_mpi_type<T>)
220
+ mpi_archive &operator &(const T &member) {
221
+ types.push_back (mpi_type<T>::get ());
222
+ MPI_Aint address{};
223
+ MPI_Get_address (&member, &address);
224
+ displacements.push_back (MPI_Aint_diff (address, base_address));
225
+ block_lengths.push_back (1 );
226
+ return *this ;
227
+ }
228
+ };
229
+
230
+ } // namespace detail
231
+
232
+ /* *
233
+ * @brief Create an `MPI_Datatype` from a serializable type.
234
+ *
235
+ * @details It is assumed that the type has a member function `serialize`
236
+ * which feeds all its class members into an archive using the `operator&`.
159
237
*
160
- * // provide a specialization of mpi_type
161
- * template <> struct mpi::mpi_type<foo> : mpi::mpi_type_from_tie<foo> {};
238
+ * @code{.cpp}
239
+ * // type to use for MPI communication
240
+ * struct foo {
241
+ * double x;
242
+ * int y;
243
+ * void serialize(auto& ar) const { ar & x & y; }
244
+ * };
162
245
* @endcode
163
246
*
164
247
* @tparam T Type to be converted to an `MPI_Datatype`.
165
248
*/
166
- template <typename T> struct mpi_type_from_tie {
167
- [[nodiscard]] static MPI_Datatype get () noexcept { return get_mpi_type (tie_data (T{})); }
249
+ template <Serializable T> [[nodiscard]] MPI_Datatype get_mpi_type (const T &obj) {
250
+ detail::mpi_archive ar (&obj);
251
+ obj.serialize (ar);
252
+ MPI_Datatype mpi_type{};
253
+ MPI_Type_create_struct (static_cast <int >(ar.block_lengths .size ()), ar.block_lengths .data (), ar.displacements .data (), ar.types .data (), &mpi_type);
254
+ MPI_Type_commit (&mpi_type);
255
+ return mpi_type;
256
+ }
257
+
258
+ /* *
259
+ * @brief Specialization of mpi::mpi_type for serializable types.
260
+ * @tparam S Serializable type.
261
+ */
262
+ template <Serializable S> struct mpi_type <S> {
263
+ [[nodiscard]] static MPI_Datatype get () noexcept {
264
+ static MPI_Datatype type = get_mpi_type (S{});
265
+ return type;
266
+ }
168
267
};
169
268
170
269
/* * @} */
0 commit comments