KaMPIng 0.2.1
(Near) zero-overhead MPI wrapper for C++
Loading...
Searching...
No Matches
struct_type.hpp
Go to the documentation of this file.
1// This file is part of KaMPIng.
2//
3// Copyright 2021-2026 The KaMPIng Authors
4//
5// KaMPIng is free software : you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
7// version. KaMPIng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
9// for more details.
10//
11// You should have received a copy of the GNU Lesser General Public License along with KaMPIng. If not, see
12// <https://www.gnu.org/licenses/>.
13
14/// @file
15/// @brief Struct-like MPI type construction via field reflection.
16
17#pragma once
18#include <tuple>
19#include <type_traits>
20#include <utility>
21
22#include <mpi.h>
23
24#include "kamping/kassert/kassert.hpp"
25#ifdef KAMPING_ENABLE_REFLECTION
26 #include <boost/pfr.hpp>
27#endif
28
32
33namespace kamping::types {
34
35/// @addtogroup kamping_types
36/// @{
37
38/// @brief Tag used for indicating that a struct is reflectable.
39/// @see struct_type
40struct kamping_tag {};
41
42/// @brief Constructs an MPI_Datatype for a struct-like type.
43/// @tparam T The type to construct the MPI_Datatype for.
44/// @tparam Lookup The lookup policy used to resolve the MPI_Datatype for each field of \p T.
45/// Defaults to \ref type_dispatcher_lookup, which uses \ref kamping::types::mpi_type_traits.
46///
47/// This requires that \p T is a `std::pair`, `std::tuple` or a type that is reflectable with
48/// [pfr](https://github.com/boostorg/pfr). If you do not agree with PFR's decision if a type is implicitly
49/// reflectable, you can override it by providing a specialization of \c pfr::is_reflectable with the tag \ref
50/// kamping_tag.
51template <typename T, typename Lookup = type_dispatcher_lookup>
53#ifdef KAMPING_ENABLE_REFLECTION
54 static_assert(
56 || boost::pfr::is_implicitly_reflectable<T, kamping_tag>::value,
57 "Type must be a std::pair, std::tuple or reflectable"
58 );
59#else
60 static_assert(
62 "Type must be a std::pair or std::tuple"
63 );
64#endif
65 /// @brief The category of the type.
66 static constexpr TypeCategory category = TypeCategory::struct_like;
67 /// @brief Whether the type has to be committed before it can be used in MPI calls.
69 /// @brief The MPI_Datatype corresponding to the type.
70 static MPI_Datatype data_type();
71};
72
73/// @}
74
75} // namespace kamping::types
76
77namespace kamping::internal {
78
79/// @brief Applies functor \p f to each field of the tuple with an index in index sequence \p Is.
80template <typename T, typename F, size_t... Is>
81void for_each_tuple_field(T&& t, F&& f, std::index_sequence<Is...>) {
82 (f(std::get<Is>(std::forward<T>(t)), Is), ...);
83}
84
85/// @brief Applies functor \p f to each field of the tuple \p t.
86template <typename T, typename F>
87void for_each_tuple_field(T& t, F&& f) {
88 for_each_tuple_field(t, std::forward<F>(f), std::make_index_sequence<std::tuple_size_v<T>>{});
89}
90
91/// @brief Applies functor \p f to each field of the tuple-like type \p t.
92/// Works for `std::pair` and `std::tuple`. If KaMPIng's reflection support is enabled, also works
93/// with types reflectable via [pfr](https://github.com/boostorg/pfr).
94template <typename T, typename F>
95void for_each_field(T& t, F&& f) {
97 for_each_tuple_field(t, std::forward<F>(f));
98 } else {
99#ifdef KAMPING_ENABLE_REFLECTION
100 boost::pfr::for_each_field(t, std::forward<F>(f));
101#else
103#endif
104 }
105}
106
107/// @brief The number of elements in a tuple-like type.
108template <typename T>
109constexpr size_t tuple_size = [] {
110 if constexpr (is_std_pair<T>::value) {
111 return 2;
112 } else if constexpr (is_std_tuple<T>::value) {
113 return std::tuple_size_v<T>;
114 } else {
115#ifdef KAMPING_ENABLE_REFLECTION
116 return boost::pfr::tuple_size_v<T>;
117#else
118 if constexpr (std::is_arithmetic_v<T>) {
119 return 1;
120 } else {
121 return std::tuple_size_v<T>;
122 }
123#endif
124 }
125}();
126
127} // namespace kamping::internal
128
129namespace kamping::types {
130
131template <typename T, typename Lookup>
133 T t{};
134 MPI_Aint base;
135 MPI_Get_address(&t, &base);
136 int blocklens[kamping::internal::tuple_size<T>];
137 MPI_Datatype mpi_types[kamping::internal::tuple_size<T>];
138 MPI_Aint disp[kamping::internal::tuple_size<T>];
139 kamping::internal::for_each_field(t, [&](auto& elem, size_t i) {
141 using elem_type = std::remove_reference_t<decltype(elem)>;
142 static_assert(
143 Lookup::template has_type_v<elem_type>,
144 "\n --> Type not supported by the current Lookup policy. "
145 "Please specialize mpi_type_traits for this type or provide a custom Lookup."
146 );
147 mpi_types[i] = Lookup::template get<elem_type>();
148 disp[i] = MPI_Aint_diff(disp[i], base);
149 blocklens[i] = 1;
150 });
151 MPI_Datatype type;
152 int err =
153 MPI_Type_create_struct(static_cast<int>(kamping::internal::tuple_size<T>), blocklens, disp, mpi_types, &type);
154 KAMPING_ASSERT(err == MPI_SUCCESS, "MPI_Type_create_struct failed");
156 err = MPI_Type_create_resized(type, 0, sizeof(T), &resized_type);
157 KAMPING_ASSERT(err == MPI_SUCCESS, "MPI_Type_create_resized failed");
158 return resized_type;
159}
160
161} // namespace kamping::types
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
TypeCategory
Type groups as defined in Section 6.9.2 of the MPI 4.0 standard.
Definition builtin_types.hpp:32
constexpr bool category_has_to_be_committed(TypeCategory category)
Returns whether an MPI_Datatype of the given category must be committed before use.
Definition builtin_types.hpp:35
Mapping of C++ datatypes to builtin MPI types.
Type traits and dispatcher for mapping C++ types to MPI datatypes.
Internal namespace marking the code that is not user-facing.
Definition collectives_helpers.hpp:20
void for_each_tuple_field(T &&t, F &&f, std::index_sequence< Is... >)
Applies functor f to each field of the tuple with an index in index sequence Is.
Definition struct_type.hpp:81
void for_each_field(T &t, F &&f)
Applies functor f to each field of the tuple-like type t. Works for std::pair and std::tuple....
Definition struct_type.hpp:95
constexpr size_t tuple_size
The number of elements in a tuple-like type.
Definition struct_type.hpp:109
Helper to check if a type is a std::pair.
Definition type_helpers.hpp:28
Helper to check if a type is a std::tuple.
Definition type_helpers.hpp:35
Tag used for indicating that a struct is reflectable.
Definition struct_type.hpp:40
Constructs an MPI_Datatype for a struct-like type.
Definition struct_type.hpp:52
static MPI_Datatype data_type()
The MPI_Datatype corresponding to the type.
Definition struct_type.hpp:132
static constexpr TypeCategory category
The category of the type.
Definition struct_type.hpp:66
static constexpr bool has_to_be_committed
Whether the type has to be committed before it can be used in MPI calls.
Definition struct_type.hpp:68
Internal type helpers for the kamping-types module.