Derived datatypes

A type_pool owns committed MPI_Datatypes: it commits a datatype on first use and frees it with the pool. The views with_pool and with_auto_pool attach a datatype from a pool to a buffer (see Views). The datatype of a type is described by specializing kamping::types::mpi_type_traits, by hand or for plain structs with kamping::types::struct_type, which derives it from the fields with Boost.PFR.

class type_pool

Registry that owns committed MPI datatypes for the lifetime of the pool.

Builtin types (e.g. int, double) are always available without registration. Derived types (e.g. contiguous or struct types) must be registered via register_type before use; the pool commits and owns the resulting MPI_Datatype.

The pool is move-only: copying is disabled because MPI_Datatype handles are not duplicable.

Defined in header <kamping/v2/type_pool.hpp>

template<typename T>
requires kamping::types::has_static_type_v<T>
MPI_Datatype register_type()
(1)
template<typename T>
MPI_Datatype register_type(MPI_Datatype dt)
(2)

(1) Registers T with the pool and returns its MPI_Datatype.

For builtin types, returns the predefined MPI type immediately without storing anything. For derived types, commits the type on the first call and returns the cached handle on subsequent calls (idempotent).

(2) Registers a caller-supplied MPI_Datatype for T, without requiring a kamping::types::mpi_type_traits<T> specialization.

T is used purely as the lookup key (std::type_index(typeid(T))) — e.g. a byte-blob type for a struct that has no trait specialization written for it. The pool wraps dt in a types::ScopedDatatype, which commits it (a no-op if already committed) and frees it on pool destruction; the pool owns the handle from this point on, the caller must not free it separately.

Idempotent: if T is already registered (by this overload or the trait-based one above), the existing handle is kept and dt is freed immediately, so calling this repeatedly with a freshly built datatype does not leak.

Precondition

dt must not be a predefined/named handle (e.g. MPI_INT) — freeing it (either via ScopedDatatype on first registration, or directly here on a redundant one) would be invalid. This overload is for derived (contiguous/struct) types only.

Template parameters:
  • T – A type satisfying has_static_type_v.

Parameters:
  • dt – The (possibly uncommitted) MPI_Datatype to register for T.

Returns:
  • (1) The committed MPI_Datatype for T.

  • (2) The committed MPI_Datatype now owned by the pool for T.

template<typename T>
std::optional<MPI_Datatype> find() const

Looks up the MPI_Datatype for T without registering it.

For builtin types, always returns the predefined MPI type. Otherwise (including types with no mpi_type_traits<T> specialization — e.g. registered only via the one-shot register_type<T>(MPI_Datatype) overload), looks T up in the pool by key and returns std::nullopt if it has not been registered.

Template parameters:
  • T – The type to look up; no trait requirement.

Returns:

The MPI_Datatype for T, or std::nullopt if not registered.

class comm_view_with_pool

Non-owning view of both an MPI_Comm and a type_pool.

Borrows its communicator handle and pool by pointer/reference — it neither frees the communicator nor destroys the pool on destruction. Cheap to copy and pass by value; use it as the standard function-parameter type in generic code that requires MPI datatype registration.

Satisfies convertible_to_mpi_handle<MPI_Comm> via mpi_handle(), so it can be passed directly to any kamping::v2:: collective or point-to-point wrapper.

Construct from a comm_view (or implicitly from MPI_Comm) and a type_pool&. The pointed-to pool must outlive this view.

// Typical class member pattern:
class MyGraph {
    kamping::v2::comm_view _comm;
    kamping::v2::type_pool _pool;

    kamping::v2::comm_view_with_pool env() { return {_comm, _pool}; }
};

// Generic function accepting a pooled comm:
void foo(kamping::v2::comm_view_with_pool comm) {
    kamping::v2::alltoallv(
        sbuf | kamping::v2::views::with_auto_pool(comm),
        kamping::v2::auto_recv_v<MyStruct>(comm),
        comm);
}

Defined in header <kamping/v2/comm_view_with_pool.hpp>

comm_view_with_pool(comm_view comm, type_pool &pool) noexcept

Construct from a comm_view and an existing pool.

Both must outlive this view. Accepts MPI_Comm implicitly via comm_view’s non-explicit constructor, so MPI_COMM_WORLD can be passed directly.

operator comm_view() const noexcept

Implicit conversion to a non-owning comm_view.

Allows passing a comm_view_with_pool wherever a comm_view is expected.

type_pool &pool() const noexcept
Returns:

Reference to the associated type pool.

int rank() const
Returns:

The rank of the calling process in this communicator.

int size() const
Returns:

The number of processes in this communicator.

group group() const

Extract the group associated with this communicator.

The returned group is owned by the caller.

Throws:
  • mpi_error – if MPI_Comm_group fails.

comm dup() const

Collective: duplicate this communicator (MPI_Comm_dup).

All processes in the communicator must call this collectively. Delegates to the free function kamping::v2::dup.

Throws:
  • mpi_error – if MPI_Comm_dup fails.

comm split(int color, int key = 0) const

Collective: split this communicator by color and key (MPI_Comm_split).

Processes with the same color form a sub-communicator ordered by key. Pass MPI_UNDEFINED as color to opt out — result is MPI_COMM_NULL. Delegates to the free function kamping::v2::split.

Throws:
  • mpi_error – if MPI_Comm_split fails.

template<typename T>
concept has_pool
template<typename T>
concept has_pool = requires(T& t) {
    { t.pool() } -> std::same_as<type_pool&>;
}

Concept matching any type that exposes a pool() method returning a type_pool&.

Satisfied by comm_view_with_pool, enabling views::with_auto_pool(env) and views::with_pool(env) to accept a pooled communicator directly.

Defined in header <kamping/v2/type_pool.hpp>