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