KaMPIng 0.1.0
(Near) zero-overhead C++ MPI bindings.
Loading...
Searching...
No Matches
data_buffer.hpp
Go to the documentation of this file.
1// This file is part of KaMPIng.
2//
3// Copyright 2021-2024 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/// The classes defined in this file serve as in, out and in/out parameters to the
16/// \c MPI calls wrapped by KaMPIng.
17///
18/// The non-modifiable buffers encapsulate input data like data to send or send counts needed for a lot of \c MPI calls.
19/// If the user already computed additional information like the send displacements or receive counts for a collective
20/// operations that would otherwise have to be computed by the library, these values can also be provided to the library
21/// via non-modifiable buffers.
22///
23/// The modifiable buffers provide memory to store the result of \c MPI calls and
24/// (intermediate information needed to complete an \c MPI call like send displacements or receive counts/displacements
25/// etc. if the user has not yet provided them). The storage can be either provided by the user or can be allocated by
26/// the library.
27///
28
29#pragma once
30
31#include <cstddef>
32#include <memory>
33#include <type_traits>
34
35#include <mpi.h>
36
37#include "kamping/assertion_levels.hpp"
40#include "kamping/kabool.hpp"
42#include "kamping/span.hpp"
43#include "kassert/kassert.hpp"
44
45namespace kamping {
46/// @addtogroup kamping_mpi_utility
47/// @{
48
49namespace internal {
50
51/// @brief Base object for parameter objects which deletes copy constructor and assignment operator and enables move.
52///
53/// You can inherit from this class privately.
54/// While constructors are never inherited, the derived class still has no copy constructor (assignment), because it
55/// can not be default constructed, due to the missing implementation in the base class. Because we provide a (default)
56/// implementation for the move constructor (assignment) in the base class, the derived class can construct default
57/// implementations.
59protected:
60 constexpr ParameterObjectBase() = default;
61 ~ParameterObjectBase() = default;
62
63 /// @brief Copy constructor is deleted as buffers should only be moved.
65 /// @brief Copy assignment operator is deleted as buffers should only be moved.
67 /// @brief Move constructor.
69 /// @brief Move assignment operator.
71
72 /// @brief Set the extracted flag to indicate that the status stored in this buffer has been moved out.
74#if KASSERT_ENABLED(KAMPING_ASSERTION_LEVEL_NORMAL)
75 is_extracted = true;
76#endif
77 }
78
79 /// @brief Throws an assertion if the extracted flag is set, i.e. the underlying status has been moved out.
80 ///
81 /// @param message The message for the assertion.
82 void kassert_not_extracted(std::string const message [[maybe_unused]]) const {
83#if KASSERT_ENABLED(KAMPING_ASSERTION_LEVEL_NORMAL)
85#endif
86 }
87
88#if KASSERT_ENABLED(KAMPING_ASSERTION_LEVEL_NORMAL)
89 bool is_extracted = false; ///< Has the status been extracted and is therefore in an invalid state?
90#endif
91};
92
93/// @brief Boolean value helping to decide if type has a \c value_type member type.
94/// @return \c true if class has \c value_type method and \c false otherwise.
95template <typename, typename = void>
96static constexpr bool has_value_type_v = false;
97
98/// @brief Boolean value helping to decide if type has a \c value_type member type.
99/// @return \c true if class has \c value_type method and \c false otherwise.
100template <typename T>
102
103/// @brief Type trait to check if a type is an instance of a templated type.
104///
105/// based on https://stackoverflow.com/a/31763111
106/// @tparam T The concrete type.
107/// @tparam Template The type template.
108/// @return \c true if the type is an instance and \c false otherwise.
109template <class T, template <class...> class Template>
110struct is_specialization : std::false_type {};
111
112/// @brief Type trait to check if a type is an instance of a templated type.
113///
114/// based on https://stackoverflow.com/a/31763111
115///
116/// A little note on how this works:
117/// - consider <tt>is_specialization<std::vector<bool, my_alloc>, std::vector></tt>
118/// - this gets template matched with the following specialization such that
119/// - <tt>Template = template<T...> std::vector<T...></tt>
120/// - <tt>Args... = bool, my_alloc</tt>
121/// - but this may only be matched in the case that <tt>Template<Args...> = std::vector<bool, my_alloc></tt>
122/// @tparam T The concrete type.
123/// @tparam Template the type template
124/// @return \c true if the type is an instance and \c false otherwise.
125template <template <class...> class Template, class... Args>
126struct is_specialization<Template<Args...>, Template> : std::true_type {};
127
128/// @brief Boolean value helping to check if a type is an instance of \c std::vector<bool>.
129/// @tparam T The type.
130/// @return \c true if \c T is an template instance of \c std::vector<bool>, \c false otherwise.
131template <typename T, typename = void>
132static constexpr bool is_vector_bool_v = false;
133
134/// @brief Boolean value helping to check if a type is an instance of \c std::vector<bool>.
135/// This catches the edge case of elements which do not have a value type, they can not be a vector bool.
136///
137/// @tparam T The type.
138/// @return \c true if \T is an template instance of \c std::vector<bool>, \c false otherwise.
139template <typename T>
140static constexpr bool is_vector_bool_v<
141 T,
142 typename std::enable_if<!has_value_type_v<std::remove_cv_t<std::remove_reference_t<T>>>>::type> = false;
143
144/// @brief Boolean value helping to check if a type is an instance of \c std::vector<bool>.
145/// @tparam T The type.
146/// @return \c true if \T is an template instance of \c std::vector<bool>, \c false otherwise.
147template <typename T>
148static constexpr bool
151 std::is_same_v<typename std::remove_cv_t<std::remove_reference_t<T>>::value_type, bool>;
152
154
155} // namespace internal
156
157/// @brief Buffer allocation tag used for indicating that a buffer should be allocated by KaMPIng.
158/// @tparam Container The container to allocate.
159///
160/// Passing this with an appropriate template parameter to a buffer creation function (such as \c recv_buf()) indicates,
161/// that the MPI operation should allocate an appropriately sized buffer of type \c Container internally.
162template <typename Container>
163struct AllocNewT {
164 /// @brief The container type to allocate.
165 using container_type = Container; ///< The container type to allocate.
166};
167
168/// @brief Convenience wrapper for creating library allocated containers. See \ref AllocNewT for details.
169template <typename Container>
170static constexpr auto alloc_new = AllocNewT<Container>{};
171
172/// @brief Helper to decide if an allocation tag is an \c AllocNewT.
173template <typename T>
174static constexpr bool is_alloc_new_v = false;
175
176/// @brief Helper to decide if an allocation tag is an \c AllocNewT.
177template <typename T>
178static constexpr bool is_alloc_new_v<AllocNewT<T>> = true;
179
180/// @brief Buffer allocation tag used for indicating that a buffer should be allocated by KaMPIng.
181/// @tparam Container A container template to use for allocation.
182///
183/// Passing this with an appropriate template parameter to a buffer creation function (such as \c recv_counts_out())
184/// indicates, that the MPI operation should allocate an appropriately sized buffer of type \c Container<T> internally,
185/// where \c T is automatically determined.
186///
187/// In case of \c recv_counts_out(alloc_new_using<std::vector>) this means, that internally, a \c std::vector<int> is
188/// allocated.
189template <template <typename...> typename Container>
191 /// @brief The container type to allocate.
192 /// @tparam Ts The template parameters for the container.
193 template <typename... Ts>
195};
196
197/// @brief Convenience wrapper for creating library allocated containers. See \ref AllocNewUsingT for details.
198template <template <typename...> typename Container>
200
201/// @brief Helper to decide if an allocation tag is an \c AllocNewUsingT.
202template <typename T>
203static constexpr bool is_alloc_new_using_v = false;
204
205/// @brief Helper to decide if an allocation tag is an \c AllocNewUsingT.
206template <template <typename...> typename Container>
208
209/// @brief Buffer allocation tag used for indicating that a buffer of type \p T should be allocated by KaMPIng.
210/// @tparam T The value type to use for the allocated buffer.
211///
212/// Passing this to a buffer creation function (such as \c recv_counts_out()) indicates, that the MPI operation should
213/// allocate an appropriately sized buffer of value type \p T internally. The allocation is deferred until the MPI
214/// operation is executed and the actual type of the container is determined by the MPI operation (usually \ref
215/// Communicator::default_container_type).
216template <typename T>
218 /// @brief The value type to use for the allocated buffer.
219 using value_type = T;
220};
221
222/// @brief Convenience wrapper for creating library allocated containers. See \ref AllocContainerOfT for details.
223template <typename T>
224static constexpr auto alloc_container_of = AllocContainerOfT<T>{};
225
226/// @brief Helper to decide if an allocation tag is an \c AllocContainerOfT.
227template <typename T>
228static constexpr bool is_alloc_container_of_v = false;
229
230/// @brief Helper to decide if an allocation tag is an \c AllocContainerOfT.
231template <typename T>
233
234namespace internal {
235/// @brief Helper to decide if data type has \c .data() method.
236/// @return \c std::true_type if class has \c .data() method and \c std::false_type otherwise.
237template <typename, typename = void>
238struct has_data_member : std::false_type {};
239
240/// @brief Helper to decide if data type has \c .data() method.
241/// @return \c std::true_type if class has \c .data() method and \c std::false_type otherwise.
242template <typename T>
243struct has_data_member<T, std::void_t<decltype(std::declval<T>().data())>> : std::true_type {};
244
245/// @brief Boolean value helping to decide if data type has \c .data() method.
246/// @return \c true if class has \c .data() method and \c false otherwise.
247template <typename T>
249
250/// @brief Enum to specify whether a buffer is modifiable
251enum class BufferModifiability { modifiable, constant };
252/// @brief Enum to specify whether a buffer owns its data
253enum class BufferOwnership { owning, referencing };
254/// @brief Enum to specify whether a buffer is allocated by the library or the user
255enum class BufferAllocation { lib_allocated, user_allocated };
256/// @brief Enum to specify whether a buffer is an in buffer of an out
257/// buffer. Out buffer will be used to directly write the result to.
258enum class BufferType { in_buffer, out_buffer, in_out_buffer, ignore };
259} // namespace internal
260
261/// @brief Enum to specify in which cases a buffer is resized.
263 no_resize, ///< Policy indicating that the underlying buffer shall never be resized.
264 grow_only, ///< Policy indicating that the underlying buffer shall only be resized if the current size
265 ///< of the buffer is too small.
266 resize_to_fit ///< Policy indicating that the underlying buffer is resized such that it has exactly the required
267 ///< size.
268};
269
271 BufferResizePolicy::no_resize; ///< Constant storing a BufferResizePolicy::no_resize enum member. It can be used to
272 ///< declare a buffer's resize policy in more concise manner.
274 BufferResizePolicy::grow_only; ///< Constant storing a BufferResizePolicy::grow_only enum member. It can be used to
275 ///< declare a buffer's resize policy in more concise manner.
277 BufferResizePolicy::resize_to_fit; ///< Constant storing a BufferResizePolicy::resize_to_fit enum member. It can be
278 ///< used to declare a buffer's resize policy in more concise manner.
279
280namespace internal {
281/// @brief Wrapper to get the value type of a non-container type (aka the type itself).
282/// @tparam has_value_type_member Whether `T` has a value_type member
283/// @tparam T The type to get the value_type of
284template <bool has_value_type_member /*= false */, typename T>
286public:
287 using value_type = T; ///< The value type of T.
288};
289
290/// @brief tag type to indicate that the value_type should be inferred from the container
292
293/// @brief Wrapper to get the value type of a container type.
294/// @tparam T The type to get the value_type of
295template <typename T>
296class ValueTypeWrapper</*has_value_type_member =*/true, T> {
297public:
298 using value_type = typename T::value_type; ///< The value type of T.
299};
300
301/// @brief for a given \tparam MemberType of a data buffer, defines the most viable resize policy.
302///
303/// For example, a single element buffer may not be resizable.
304template <typename MemberType>
306 auto is_single_element = !has_data_member_v<MemberType>;
307 if (is_single_element || !has_member_resize_v<MemberType, size_t>) {
308 return no_resize;
309 } else {
310 return resize_to_fit;
311 }
312}();
313
314/// @brief Data buffer used for named parameters.
315///
316/// DataBuffer wraps all buffer storages provided by an std-like container like std::vector or single values. A
317/// Container type must provide \c data(), \c size() and expose the type definition \c value_type.
318/// @tparam MemberType Container or data type on which this buffer is based.
319/// @tparam TParameterType Type of the parameter_type_param (required for parameter selection within plugins).
320/// @tparam parameter_type_param Parameter type represented by this buffer.
321/// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
322/// modify the underlying container. `constant` otherwise.
323/// @tparam ownership `owning` if the buffer should hold the actual container.
324/// `referencing` if only a reference to an existing container should be held.
325/// @tparam buffer_type_param Type of buffer, i.e., \c in_buffer, \c out_buffer, or \c in_out_buffer.
326/// @tparam buffer_resize_policy_param Policy specifying whether (and if so, how) the underlying buffer shall be
327/// resized.
328/// @tparam allocation `lib_allocated` if the buffer was allocated by the library,
329/// @tparam ValueType requested value_type for the buffer. If it does not match the containers value type, compilation
330/// fails. By default, this is set to \c default_value_type_tag and the value_type is inferred from the underlying
331/// container, without any checking `user_allocated` if it was allocated by the user.
332template <
333 typename MemberType,
334 typename TParameterType,
340 BufferAllocation allocation = BufferAllocation::user_allocated,
341 typename ValueType = default_value_type_tag>
343public:
345 parameter_type_param; ///< The type of parameter this buffer represents.
346
347 static constexpr BufferType buffer_type = buffer_type_param; ///< The type of the buffer, i.e., in, out, or in_out.
348
350 buffer_resize_policy_param; ///< The policy specifying in which cases the buffer shall be resized.
351
352 /// @brief \c true if the buffer is an out or in/out buffer that results will be written to and \c false
353 /// otherwise.
354 static constexpr bool is_out_buffer =
355 (buffer_type_param == BufferType::out_buffer || buffer_type_param == BufferType::in_out_buffer);
356
357 /// @brief Indicates whether the buffer is allocated by KaMPIng.
358 static constexpr bool is_lib_allocated = allocation == BufferAllocation::lib_allocated;
359
360 static constexpr bool is_owning =
361 ownership == BufferOwnership::owning; ///< Indicates whether the buffer owns its underlying storage.
362
363 static constexpr bool is_modifiable =
364 modifiability == BufferModifiability::modifiable; ///< Indicates whether the underlying storage is modifiable.
365 static constexpr bool is_single_element =
366 !has_data_member_v<MemberType>; ///<`true` if the DataBuffer represents a singe element, `false` if the
367 ///< DataBuffer represents a container.
369 std::conditional_t<is_modifiable, MemberType, MemberType const>; ///< The ContainerType as const or
370 ///< non-const depending on
371 ///< modifiability.
372
373 // We can not do the check for std::vector<bool> here, because to use a DataBuffer of std::vector<bool> as an unused
374 // default parameter is allowed, as long the buffer is never used. Therefore the check for std::vector<bool> happens
375 // only when the underlying member is actually accessed and the corresponding accessor method is instantiated.
376 // static_assert(
377 // !is_vector_bool_v<MemberType>,
378 // "Buffers based on std::vector<bool> are not supported, use std::vector<kamping::kabool> instead.");
379
380 using MemberTypeWithConstAndRef = std::conditional_t<
381 ownership == BufferOwnership::owning,
383 MemberTypeWithConst&>; ///< The ContainerType as const or non-const (see ContainerTypeWithConst) and
384 ///< reference or non-reference depending on ownership.
385
387 typename ValueTypeWrapper<!is_single_element, MemberType>::value_type; ///< Value type of the buffer.
388 static_assert(
389 std::is_same_v<ValueType, default_value_type_tag> || std::is_same_v<ValueType, value_type>,
390 "The requested value type of the buffer does not match the value type of the underlying container"
391 );
393 std::conditional_t<is_modifiable, value_type, value_type const>; ///< Value type as const or non-const depending
394 ///< on modifiability
395 static_assert(
397 "A constant data buffer requires the that the resize policy is no_resize."
398 );
399 static_assert(
401 "A single element data buffer requires the that the resize policy is no_resize."
402 );
403 static_assert(
406 "The underlying container does not provide a resize function, which is required by the resize policy."
407 );
408
409 /// @brief Constructor for referencing ContainerBasedBuffer.
410 /// @param container Container holding the actual data.
411 template <bool enabled = ownership == BufferOwnership::referencing, std::enable_if_t<enabled, bool> = true>
413
414 /// @brief Constructor for owning ContainerBasedBuffer.
415 /// @param container Container holding the actual data.
416 template <bool enabled = ownership == BufferOwnership::owning, std::enable_if_t<enabled, bool> = true>
418
419 /// @brief Constructor for lib allocated ContainerBasedBuffer.
420 template <bool enabled = allocation == BufferAllocation::lib_allocated, std::enable_if_t<enabled, bool> = true>
421 DataBuffer() : _data() {
422 static_assert(ownership == BufferOwnership::owning, "Lib allocated buffers must be owning");
423 static_assert(is_modifiable, "Lib allocated buffers must be modifiable");
424 }
425
426 /// @brief The size of the underlying container.
427 size_t size() const {
428 kassert_not_extracted("Cannot get the size of a buffer that has already been extracted.");
429 if constexpr (is_single_element) {
430 return 1;
431 } else {
432 return underlying().size();
433 }
434 }
435
436 /// @brief Resizes the underlying container such that it holds exactly \c size elements of \c value_type.
437 ///
438 /// This function calls \c resize on the underlying container.
439 ///
440 /// This takes only part in overload resolution if the \ref resize_policy of the buffer is \c resize_to_fit.
441 ///
442 /// @param size Size the container is resized to.
443 template <
445 typename std::enable_if_t<_resize_policy == resize_to_fit, bool> = true>
446 void resize(size_t size) {
447 kassert_not_extracted("Cannot resize a buffer that has already been extracted.");
448 underlying().resize(size);
449 }
450
451 /// @brief Resizes the underlying container such that it holds at least \c size elements of \c value_type.
452 ///
453 /// This function calls \c resize on the underlying container, but only if the requested \param size is larger than
454 /// the current buffer size. Otherwise, the buffer is left unchanged.
455 ///
456 /// This takes only part in overload resolution if the \ref resize_policy of the buffer is \c grow_only.
457 ///
458 template <
460 typename std::enable_if_t<_resize_policy == grow_only, bool> = true>
461 void resize(size_t size) {
462 kassert_not_extracted("Cannot resize a buffer that has already been extracted.");
463 if (this->size() < size) {
464 underlying().resize(size);
465 }
466 }
467
468 template <
470 typename std::enable_if_t<_resize_policy == no_resize, bool> = true>
471 void resize(size_t size) = delete;
472
473 /// @brief Resizes the underlying container if the buffer the buffer's resize policy allows and resizing is
474 /// necessary.
475 ///
476 /// @tparam SizeFunc Type of the functor which computes the required buffer size.
477 /// @param compute_required_size Functor which is used to compute the required buffer size. compute_required_size()
478 /// is not called if the buffer's resize policy is BufferResizePolicy::no_resize.
479 template <typename SizeFunc>
485
486 /// @brief Get const access to the underlying container.
487 /// @return Pointer to the underlying container.
488 value_type const* data() const {
489 kassert_not_extracted("Cannot get a pointer to a buffer that has already been extracted.");
490 if constexpr (is_single_element) {
491 return &underlying();
492 } else {
493 return std::data(underlying());
494 }
495 }
496
497 /// @brief Get access to the underlying container.
498 /// @return Pointer to the underlying container.
500 kassert_not_extracted("Cannot get a pointer to a buffer that has already been extracted.");
501 if constexpr (is_single_element) {
502 return &underlying();
503 } else {
504 return std::data(underlying());
505 }
506 }
507
508 /// @brief Get read-only access to the underlying storage.
509 /// @return Span referring the underlying storage.
511 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
512 return {this->data(), this->size()};
513 }
514
515 /// @brief Get access to the underlying storage.
516 /// @return Span referring to the underlying storage.
518 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
519 return {this->data(), this->size()};
520 }
521
522 /// @brief Get the single element wrapped by this object.
523 /// @return The single element wrapped by this object.
524 template <bool enabled = is_single_element, std::enable_if_t<enabled, bool> = true>
526 kassert_not_extracted("Cannot get an element from a buffer that has already been extracted.");
527 return underlying();
528 }
529
530 /// @brief Provides access to the underlying data.
531 /// @return A reference to the data.
532 MemberType const& underlying() const {
533 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
534 // this assertion is only checked if the buffer is actually accessed.
535 static_assert(
537 "Buffers based on std::vector<bool> are not supported, use std::vector<kamping::kabool> instead."
538 );
539 return _data;
540 }
541
542 /// @brief Provides access to the underlying data.
543 /// @return A reference to the data.
544 template <bool enabled = modifiability == BufferModifiability::modifiable, std::enable_if_t<enabled, bool> = true>
546 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
547 // this assertion is only checked if the buffer is actually accessed.
548 static_assert(
550 "Buffers based on std::vector<bool> are not supported, use std::vector<kamping::kabool> instead."
551 );
552 return _data;
553 }
554
555 /// @brief Extract the underlying container. This will leave the DataBuffer in an unspecified
556 /// state.
557 ///
558 /// @return Moves the underlying container out of the DataBuffer.
559 template <bool enabled = is_owning, std::enable_if_t<enabled, bool> = true>
561 static_assert(
562 ownership == BufferOwnership::owning,
563 "Moving out of a reference should not be done because it would leave "
564 "a users container in an unspecified state."
565 );
566 kassert_not_extracted("Cannot extract a buffer that has already been extracted.");
567 auto extracted = std::move(underlying());
568 // we set is_extracted here because otherwise the call to underlying() would fail
570 return extracted;
571 }
572
573private:
574 MemberTypeWithConstAndRef _data; ///< Container which holds the actual data.
575};
576
577/// @brief A more generic version of a DataBuffer which stores an object of type \tparam MemberType with its associcated
578/// \tparam ParameterType. In difference to \ref DataBuffer, GenericDataBuffer does not require the wrapped object to
579/// expose neither \c data(), \c resize() nor \c value_type.
580///
581/// @tparam MemberType Type of the wrapped object.
582/// @tparam TParameterType Type of the parameter_type_param (required for parameter selection within plugins).
583/// @tparam parameter_type_param Parameter type represented by this buffer.
584/// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
585/// modify the underlying container. `constant` otherwise.
586/// @tparam ownership `owning` if the buffer should hold the object.
587/// `referencing` if only a reference to an existing object should be held.
588/// @tparam buffer_type_param Type of buffer, i.e., \c in_buffer, \c out_buffer, or \c in_out_buffer.
589template <
590 typename MemberType,
591 typename TParameterType,
597public:
599 parameter_type_param; ///< The type of parameter this buffer represents.
600
601 static constexpr BufferType buffer_type = buffer_type_param; ///< The type of the buffer, i.e., in, out, or in_out.
602
603 /// @brief \c true if the buffer is an out or in/out buffer that results will be written to and \c false
604 /// otherwise.
605 static constexpr bool is_out_buffer =
606 (buffer_type_param == BufferType::out_buffer || buffer_type_param == BufferType::in_out_buffer);
607
608 static constexpr bool is_owning =
609 ownership == BufferOwnership::owning; ///< Indicates whether the buffer owns its underlying storage.
610
611 static constexpr bool is_modifiable =
612 modifiability == BufferModifiability::modifiable; ///< Indicates whether the underlying storage is modifiable.
613
614 using value_type = MemberType; ///< Value type of the buffer.
615
617 std::conditional_t<is_modifiable, MemberType, MemberType const>; ///< The ContainerType as const or
618 ///< non-const depending on
619 ///< modifiability.
620
621 using MemberTypeWithConstAndRef = std::conditional_t<
622 ownership == BufferOwnership::owning,
624 MemberTypeWithConst&>; ///< The ContainerType as const or non-const (see ContainerTypeWithConst) and
625 ///< reference or non-reference depending on ownership.
626
627 /// @brief Constructor for referencing GenericDataBuffer.
628 /// @param container Container holding the actual data.
629 template <bool enabled = ownership == BufferOwnership::referencing, std::enable_if_t<enabled, bool> = true>
631
632 /// @brief Constructor for owning GenericDataBuffer.
633 /// @param container Container holding the actual data.
634 template <bool enabled = ownership == BufferOwnership::owning, std::enable_if_t<enabled, bool> = true>
636
637 /// @brief Provides access to the underlying data.
638 /// @return A reference to the data.
639 MemberType const& underlying() const {
640 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
641 return _data;
642 }
643
644 /// @brief Provides access to the underlying data.
645 /// @return A reference to the data.
646 template <bool enabled = modifiability == BufferModifiability::modifiable, std::enable_if_t<enabled, bool> = true>
648 kassert_not_extracted("Cannot get a buffer that has already been extracted.");
649 return _data;
650 }
651
652 /// @brief Extract the underlying container. This will leave the DataBuffer in an unspecified
653 /// state.
654 ///
655 /// @return Moves the underlying container out of the DataBuffer.
656 template <bool enabled = is_owning, std::enable_if_t<enabled, bool> = true>
658 static_assert(
659 ownership == BufferOwnership::owning,
660 "Moving out of a reference should not be done because it would leave "
661 "a users container in an unspecified state."
662 );
663 kassert_not_extracted("Cannot extract a buffer that has already been extracted.");
664 auto extracted = std::move(underlying());
665 // we set is_extracted here because otherwise the call to underlying() would fail
667 return extracted;
668 }
669
670private:
671 MemberTypeWithConstAndRef _data; ///< The wrapped object.
672};
673
674/// @brief Empty buffer that can be used as default argument for optional buffer parameters.
675/// @tparam ParameterType Parameter type represented by this pseudo buffer.
676template <typename Data, ParameterType type, BufferType buffer_type_param>
678public:
679 static constexpr ParameterType parameter_type = type; ///< The type of parameter this buffer represents.
680 static constexpr bool is_modifiable =
681 false; ///< This pseudo buffer is not modifiable since it represents no actual buffer.
682 using value_type = Data; ///< Value type of the buffer.
683 static constexpr BufferType buffer_type =
684 buffer_type_param; ///< The type of the buffer, usually ignore for this special buffer.
685
686 static constexpr BufferResizePolicy resize_policy = no_resize; ///< An empty buffer can not be resized.
687 static constexpr bool is_out_buffer = false; ///< An empty buffer is never output.
688 static constexpr bool is_lib_allocated = false; ///< An empty buffer is not allocated.
689 static constexpr bool is_single_element = false; ///< An empty buffer contains no elements.
690 static constexpr bool is_owning = false; ///< An empty buffer does not own anything.
691
692 /// @brief Get the number of elements in the underlying storage.
693 /// @return Number of elements in the underlying storage (always 0).
694 size_t size() const {
695 return 0;
696 }
697
698 /// @brief Get a nullptr.
699 /// @return nullptr.
700 value_type const* data() const {
701 return nullptr;
702 }
703
704 /// @brief Returns a span containing a nullptr.
705 /// @return Span containing a nullptr.
707 return {};
708 }
709 /// @brief Resizes the underlying container if the buffer the buffer's resize policy allows and resizing is
710 /// necessary. Does nothing for an empty buffer.
711 ///
712 /// @tparam SizeFunc Type of the functor which computes the required buffer size.
713 /// @param compute_required_size Functor which is used to compute the required buffer size. compute_required_size()
714 /// is not called if the buffer's resize policy is BufferResizePolicy::no_resize.
715 template <typename SizeFunc>
717};
718
719/// @brief Helper to decide if a type is an instance of \c EmptyDataBuffer.
720template <typename T>
721constexpr bool is_empty_data_buffer_v = false;
722
723/// @brief Helper to decide if a type is an instance of \c EmptyDataBuffer.
724template <typename T, ParameterType type, BufferType buffer_type_param>
726
727///
728/// @brief Creates a user allocated DataBuffer containing the supplied data (a container or a single element)
729///
730/// Creates a user allocated DataBuffer with the given template parameters and ownership based on whether an rvalue or
731/// lvalue reference is passed.
732///
733/// @tparam TParameterType type of parameter type represented by this buffer.
734/// @tparam parameter_type parameter type represented by this buffer.
735/// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
736/// modify the underlying container. `constant` otherwise.
737/// @tparam buffer_type Type of this buffer, i.e., in, out, or in_out.
738/// @tparam Data Container or data type on which this buffer is based.
739/// @tparam buffer_resize_policy Policy specifying whether (and if so, how) the underlying buffer shall be resized.
740/// @tparam ValueType Requested value type for the the data buffer. If not specified, it will be deduced from the
741/// underlying container and no checking is performed.
742/// @param data Universal reference to a container or single element holding the data for the buffer.
743///
744/// @return A user allocated DataBuffer with the given template parameters and matching ownership.
745template <
746 typename TParameterType,
747 TParameterType parameter_type,
749 BufferType buffer_type,
752 typename Data>
753auto make_data_buffer(Data&& data) {
754 constexpr BufferOwnership ownership =
755 std::is_rvalue_reference_v<Data&&> ? BufferOwnership::owning : BufferOwnership::referencing;
756
757 // Make sure that Data is const, the buffer created is constant (so we don't really remove constness in the return
758 // statement below).
759 constexpr bool is_const_data_type = std::is_const_v<std::remove_reference_t<Data>>;
760 constexpr bool is_const_buffer = modifiability == BufferModifiability::constant;
761 // Implication: is_const_data_type => is_const_buffer.
762 static_assert(!is_const_data_type || is_const_buffer);
763 return DataBuffer<
764 std::remove_const_t<std::remove_reference_t<Data>>,
766 parameter_type,
768 ownership,
769 buffer_type,
771 BufferAllocation::user_allocated,
772 ValueType>(std::forward<Data>(data));
773}
774
775/// @brief Creates a library allocated DataBuffer with the given container or single data type.
776///
777/// Creates a library allocated DataBuffer with the given template parameters.
778///
779/// @tparam TParameterType type of parameter type represented by this buffer.
780/// @tparam parameter_type parameter type represented by this buffer.
781/// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
782/// modify the underlying container. `constant` otherwise.
783/// @tparam buffer_type Type of this buffer, i.e., in, out, or in_out.
784/// @tparam buffer_resize_policy Policy specifying whether (and if so, how) the underlying buffer shall be resized.
785/// @tparam ValueType Requested value type for the the data buffer. If not specified, it will be deduced from the
786/// underlying container and no checking is performed.
787/// @tparam Data Container or data type on which this buffer is based.
788///
789/// @return A library allocated DataBuffer with the given template parameters.
790template <
791 typename TParameterType,
792 TParameterType parameter_type,
794 BufferType buffer_type,
796 typename ValueType = default_value_type_tag,
797 typename Data>
799 return DataBuffer<
800 Data,
802 parameter_type,
803 BufferModifiability::modifiable, // something library allocated is always modifiable
804 BufferOwnership::owning,
805 buffer_type,
807 BufferAllocation::lib_allocated,
808 ValueType>();
809}
810
811/// @brief Creates a library allocated DataBuffer by instantiating the given container template with the given value
812/// type.
813///
814///
815/// @tparam TParameterType type of parameter type represented by this buffer.
816/// @tparam parameter_type parameter type represented by this buffer.
817/// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
818/// modify the underlying container. `constant` otherwise.
819/// @tparam buffer_type Type of this buffer, i.e., in, out, or in_out.
820/// @tparam buffer_resize_policy Policy specifying whether (and if so, how) the underlying buffer shall be resized.
821/// @tparam ValueType The value type to initialize the \c Data template with. If not specified, this will fail.
822/// @tparam Data Container template this buffer is based on. The first template parameter is initialized with \c
823/// ValueType
824///
825/// @return A library allocated DataBuffer with the given template parameters.
826template <
827 typename TParameterType,
828 ParameterType parameter_type,
830 BufferType buffer_type,
832 typename ValueType = default_value_type_tag,
833 template <typename...>
834 typename Data>
836 // this check prevents that this factory function is used, when the value type is not known
837 static_assert(
838 !std::is_same_v<ValueType, default_value_type_tag>,
839 "Value type for new library allocated container can not be deduced."
840 );
841 return DataBuffer<
844 parameter_type,
845 BufferModifiability::modifiable, // something library allocated is always modifiable
846 BufferOwnership::owning,
847 buffer_type,
849 BufferAllocation::lib_allocated,
850 ValueType>();
851}
852
853// /// @brief Creates an owning DataBuffer containing the supplied data in a std::vector.
854// ///
855// /// Creates an owning DataBuffer with the given template parameters.
856// ///
857// /// An initializer list of type \c bool will be converted to a \c std::vector<kamping::kabool>.
858// ///
859// /// @tparam parameter_type parameter type represented by this buffer.
860// /// @tparam modifiability `modifiable` if a KaMPIng operation is allowed to
861// /// modify the underlying container. `constant` otherwise.
862// /// @tparam buffer_type Type of this buffer, i.e., in, out, or in_out.
863// /// @tparam buffer_resize_policy Policy specifying whether (and if so, how) the underlying buffer shall be resized.
864// /// @tparam Data Container or data type on which this buffer is based.
865// /// @param data std::initializer_list holding the data for the buffer.
866// ///
867// /// @return A library allocated DataBuffer with the given template parameters.
868// template <
869// ParameterType parameter_type,
870// BufferModifiability modifiability,
871// BufferType buffer_type,
872// BufferResizePolicy buffer_resize_policy,
873// typename Data>
874// auto make_data_buffer(std::initializer_list<Data> data) {
875// // auto data_vec = [&]() {
876// // if constexpr (std::is_same_v<Data, bool>) {
877// // return std::vector<kabool>(data.begin(), data.end());
878// // // We only use automatic conversion of bool to kabool for initializer lists, but not for single
879// elements of
880// // // type bool. The reason for that is, that sometimes single element conversion may not be desired.
881// // // E.g. consider a gather operation with send_buf := bool& and recv_buf := Span<bool>, or a bcast with
882// // // send_recv_buf = bool&
883// // } else {
884// // return std::vector<Data>{data};
885// // }
886// // }();
887// return DataBuffer<
888// decltype(data_vec),
889// parameter_type,
890// modifiability,
891// BufferOwnership::owning,
892// buffer_type,
893// buffer_resize_policy,
894// BufferAllocation::user_allocated>(std::move(data_vec));
895// }
896
897} // namespace internal
898
899/// @}
900
901} // namespace kamping
Helper functions that make casts safer.
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
Data buffer used for named parameters.
Definition data_buffer.hpp:342
DataBuffer(MemberType container)
Constructor for owning ContainerBasedBuffer.
Definition data_buffer.hpp:417
value_type const * data() const
Get const access to the underlying container.
Definition data_buffer.hpp:488
static constexpr bool is_lib_allocated
Indicates whether the buffer is allocated by KaMPIng.
Definition data_buffer.hpp:358
void resize_if_requested(SizeFunc &&compute_required_size)
Resizes the underlying container if the buffer the buffer's resize policy allows and resizing is nece...
Definition data_buffer.hpp:480
Span< value_type const > get() const
Get read-only access to the underlying storage.
Definition data_buffer.hpp:510
static constexpr TParameterType parameter_type
The type of parameter this buffer represents.
Definition data_buffer.hpp:344
value_type const get_single_element() const
Get the single element wrapped by this object.
Definition data_buffer.hpp:525
std::conditional_t< ownership==BufferOwnership::owning, MemberTypeWithConst, MemberTypeWithConst & > MemberTypeWithConstAndRef
Definition data_buffer.hpp:380
static constexpr bool is_single_element
Definition data_buffer.hpp:365
static constexpr bool is_owning
Indicates whether the buffer owns its underlying storage.
Definition data_buffer.hpp:360
Span< value_type_with_const > get()
Get access to the underlying storage.
Definition data_buffer.hpp:517
static constexpr BufferType buffer_type
The type of the buffer, i.e., in, out, or in_out.
Definition data_buffer.hpp:347
static constexpr BufferResizePolicy resize_policy
The policy specifying in which cases the buffer shall be resized.
Definition data_buffer.hpp:349
size_t size() const
The size of the underlying container.
Definition data_buffer.hpp:427
value_type_with_const * data()
Get access to the underlying container.
Definition data_buffer.hpp:499
MemberType const & underlying() const
Provides access to the underlying data.
Definition data_buffer.hpp:532
static constexpr bool is_modifiable
Indicates whether the underlying storage is modifiable.
Definition data_buffer.hpp:363
DataBuffer(MemberTypeWithConst &container)
Constructor for referencing ContainerBasedBuffer.
Definition data_buffer.hpp:412
void resize(size_t size)
Resizes the underlying container such that it holds exactly size elements of value_type.
Definition data_buffer.hpp:446
std::conditional_t< is_modifiable, MemberType, MemberType const > MemberTypeWithConst
Definition data_buffer.hpp:368
typename ValueTypeWrapper<!is_single_element, MemberType >::value_type value_type
Value type of the buffer.
Definition data_buffer.hpp:386
std::conditional_t< is_modifiable, value_type, value_type const > value_type_with_const
Definition data_buffer.hpp:392
static constexpr bool is_out_buffer
true if the buffer is an out or in/out buffer that results will be written to and false otherwise.
Definition data_buffer.hpp:354
DataBuffer()
Constructor for lib allocated ContainerBasedBuffer.
Definition data_buffer.hpp:421
MemberType & underlying()
Provides access to the underlying data.
Definition data_buffer.hpp:545
MemberTypeWithConst extract()
Extract the underlying container. This will leave the DataBuffer in an unspecified state.
Definition data_buffer.hpp:560
Empty buffer that can be used as default argument for optional buffer parameters.
Definition data_buffer.hpp:677
static constexpr BufferType buffer_type
The type of the buffer, usually ignore for this special buffer.
Definition data_buffer.hpp:683
size_t size() const
Get the number of elements in the underlying storage.
Definition data_buffer.hpp:694
static constexpr bool is_single_element
An empty buffer contains no elements.
Definition data_buffer.hpp:689
static constexpr bool is_modifiable
This pseudo buffer is not modifiable since it represents no actual buffer.
Definition data_buffer.hpp:680
Span< value_type > get() const
Returns a span containing a nullptr.
Definition data_buffer.hpp:706
static constexpr bool is_out_buffer
An empty buffer is never output.
Definition data_buffer.hpp:687
value_type const * data() const
Get a nullptr.
Definition data_buffer.hpp:700
static constexpr ParameterType parameter_type
The type of parameter this buffer represents.
Definition data_buffer.hpp:679
static constexpr bool is_lib_allocated
An empty buffer is not allocated.
Definition data_buffer.hpp:688
static constexpr bool is_owning
An empty buffer does not own anything.
Definition data_buffer.hpp:690
void resize_if_requested(SizeFunc &&compute_required_size)
Resizes the underlying container if the buffer the buffer's resize policy allows and resizing is nece...
Definition data_buffer.hpp:716
static constexpr BufferResizePolicy resize_policy
An empty buffer can not be resized.
Definition data_buffer.hpp:686
A more generic version of a DataBuffer which stores an object of type.
Definition data_buffer.hpp:596
MemberType const & underlying() const
Provides access to the underlying data.
Definition data_buffer.hpp:639
static constexpr BufferType buffer_type
The type of the buffer, i.e., in, out, or in_out.
Definition data_buffer.hpp:601
std::conditional_t< is_modifiable, MemberType, MemberType const > MemberTypeWithConst
Definition data_buffer.hpp:616
std::conditional_t< ownership==BufferOwnership::owning, MemberTypeWithConst, MemberTypeWithConst & > MemberTypeWithConstAndRef
Definition data_buffer.hpp:621
static constexpr bool is_out_buffer
true if the buffer is an out or in/out buffer that results will be written to and false otherwise.
Definition data_buffer.hpp:605
GenericDataBuffer(MemberType container)
Constructor for owning GenericDataBuffer.
Definition data_buffer.hpp:635
GenericDataBuffer(MemberTypeWithConst &container)
Constructor for referencing GenericDataBuffer.
Definition data_buffer.hpp:630
MemberTypeWithConst extract()
Extract the underlying container. This will leave the DataBuffer in an unspecified state.
Definition data_buffer.hpp:657
static constexpr TParameterType parameter_type
The type of parameter this buffer represents.
Definition data_buffer.hpp:598
static constexpr bool is_owning
Indicates whether the buffer owns its underlying storage.
Definition data_buffer.hpp:608
MemberType & underlying()
Provides access to the underlying data.
Definition data_buffer.hpp:647
static constexpr bool is_modifiable
Indicates whether the underlying storage is modifiable.
Definition data_buffer.hpp:611
Base object for parameter objects which deletes copy constructor and assignment operator and enables ...
Definition data_buffer.hpp:58
void set_extracted()
Set the extracted flag to indicate that the status stored in this buffer has been moved out.
Definition data_buffer.hpp:73
ParameterObjectBase & operator=(ParameterObjectBase const &)=delete
Copy assignment operator is deleted as buffers should only be moved.
ParameterObjectBase & operator=(ParameterObjectBase &&)=default
Move assignment operator.
ParameterObjectBase(ParameterObjectBase &&)=default
Move constructor.
void kassert_not_extracted(std::string const message) const
Throws an assertion if the extracted flag is set, i.e. the underlying status has been moved out.
Definition data_buffer.hpp:82
ParameterObjectBase(ParameterObjectBase const &)=delete
Copy constructor is deleted as buffers should only be moved.
typename T::value_type value_type
The value type of T.
Definition data_buffer.hpp:298
Wrapper to get the value type of a non-container type (aka the type itself).
Definition data_buffer.hpp:285
T value_type
The value type of T.
Definition data_buffer.hpp:287
constexpr int normal
Default assertion level. This level is used if no assertion level is specified.
Definition assertion_levels.hpp:19
constexpr BufferResizePolicy grow_only
Definition data_buffer.hpp:273
constexpr BufferResizePolicy resize_to_fit
Definition data_buffer.hpp:276
static constexpr bool is_alloc_new_using_v
Helper to decide if an allocation tag is an AllocNewUsingT.
Definition data_buffer.hpp:203
static constexpr auto alloc_container_of
Convenience wrapper for creating library allocated containers. See AllocContainerOfT for details.
Definition data_buffer.hpp:224
BufferResizePolicy
Enum to specify in which cases a buffer is resized.
Definition data_buffer.hpp:262
static constexpr bool is_alloc_new_v
Helper to decide if an allocation tag is an AllocNewT.
Definition data_buffer.hpp:174
static constexpr bool is_alloc_container_of_v
Helper to decide if an allocation tag is an AllocContainerOfT.
Definition data_buffer.hpp:228
static constexpr auto alloc_new_using
Convenience wrapper for creating library allocated containers. See AllocNewUsingT for details.
Definition data_buffer.hpp:199
constexpr BufferResizePolicy no_resize
Definition data_buffer.hpp:270
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:170
@ no_resize
Policy indicating that the underlying buffer shall never be resized.
ParameterType
Each input parameter to one of the MPI calls wrapped by KaMPIng needs to has one of the following tag...
Definition named_parameter_types.hpp:33
Macros for generating concept-like type traits to check for member functions of objects.
#define KAMPING_MAKE_HAS_MEMBER(Member)
Macro for generating has_member_xxx and has_member_xxx_v templates. They return true if the type give...
Definition has_member.hpp:91
File containing the parameter types used by the KaMPIng library.
constexpr bool has_data_member_v
Boolean value helping to decide if data type has .data() method.
Definition data_buffer.hpp:248
constexpr BufferResizePolicy maximum_viable_resize_policy
for a given
Definition data_buffer.hpp:305
static constexpr bool is_vector_bool_v
Boolean value helping to check if a type is an instance of std::vector<bool>.
Definition data_buffer.hpp:132
static constexpr bool has_value_type_v
Boolean value helping to decide if type has a value_type member type.
Definition data_buffer.hpp:96
BufferAllocation
Enum to specify whether a buffer is allocated by the library or the user.
Definition data_buffer.hpp:255
constexpr bool is_empty_data_buffer_v
Helper to decide if a type is an instance of EmptyDataBuffer.
Definition data_buffer.hpp:721
BufferOwnership
Enum to specify whether a buffer owns its data.
Definition data_buffer.hpp:253
BufferModifiability
Enum to specify whether a buffer is modifiable.
Definition data_buffer.hpp:251
auto make_data_buffer(Data &&data)
Creates a user allocated DataBuffer containing the supplied data (a container or a single element)
Definition data_buffer.hpp:753
BufferType
Enum to specify whether a buffer is an in buffer of an out buffer. Out buffer will be used to directl...
Definition data_buffer.hpp:258
STL namespace.
constexpr internal::ignore_t< T > ignore
Tag for parameters that can be omitted on some PEs (e.g., root PE, or non-root PEs).
Definition parameter_objects.hpp:570
Buffer allocation tag used for indicating that a buffer of type T should be allocated by KaMPIng.
Definition data_buffer.hpp:217
T value_type
The value type to use for the allocated buffer.
Definition data_buffer.hpp:219
Buffer allocation tag used for indicating that a buffer should be allocated by KaMPIng.
Definition data_buffer.hpp:163
Buffer allocation tag used for indicating that a buffer should be allocated by KaMPIng.
Definition data_buffer.hpp:190
tag type to indicate that the value_type should be inferred from the container
Definition data_buffer.hpp:291
Helper to decide if data type has .data() method.
Definition data_buffer.hpp:238
Type trait to check if a type is an instance of a templated type.
Definition data_buffer.hpp:110