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