H5CPP  v1.14.0
Modern C++ templates for HDF5 serial and parallel I/O
Loading...
Searching...
No Matches
Conversion Policy

h5cpp's managed handle types — every wrapper catalogued in Handles, Descriptors & Property Lists (the object handles h5::fd_t / h5::ds_t / h5::gr_t / h5::at_t / h5::ob_t / h5::sp_t, the datatype template h5::dt_t<T>, the reference handle h5::reference_t, and every property-list wrapper h5::fapl_t / h5::fcpl_t / h5::dapl_t / …) — wrap HDF5's raw hid_t integer ID in an RAII container that owns its lifetime. The conversion policy is the contract for how those wrappers and the raw hid_t interconvert at call sites that mix h5cpp and the HDF5 C API.

Three orthogonal axes, each toggleable at compile time:

AxisDefaultOpt-out macroEffect when disabled
to-CAPI   (h5::fd_thid_t) enabled (H5CPP_CONVERSION_TO_CAPI) -DH5CPP_CONVERSION_TO_CAPI_DISABLED conversion operator removed; passing a wrapper to the C API does not compile
from-CAPI   (hid_th5::fd_t) enabled (H5CPP_CONVERSION_FROM_CAPI) -DH5CPP_CONVERSION_FROM_CAPI_DISABLED constructor removed; cannot wrap a raw hid_t returned by HDF5
implicit vs explicit   (cast required?) implicit (H5CPP_CONVERSION_IMPLICIT) -DH5CPP_CONVERSION_EXPLICIT static_cast<hid_t>(fd) required at every call site that needs the raw handle

The implementation lives in h5cpp/H5config.hpp lines 53-64 (the three #ifndef / #define blocks that flip the macros) and h5cpp/H5Iall.hpp lines 16-20 (the H5CPP__EXPLICIT selector that the wrapper template uses to qualify the conversion operator).

Default behaviour (recommended)

With no opt-out macros set — the default in 95% of consumer code — both directions are wired and the conversion is implicit:

h5::fd_t fd = h5::create("file.h5", H5F_ACC_TRUNC); // wrapper from h5cpp
H5Dcreate(fd, "ds", H5T_NATIVE_FLOAT, ... ); // implicit: fd → hid_t
h5::ds_t ds(some_raw_hid_t); // implicit: hid_t → ds_t
h5::at_t create(const hid_t &parent, const std::string &path, args_t &&... args)
Create a new attribute of element type T on a parent HDF5 object.
Definition H5Acreate.hpp:100
T aread(const hid_t &ds, const std::string &name, const h5::acpl_t &acpl=h5::default_acpl)
Read an attribute by name and return its value as type T.
Definition H5Aread.hpp:76

The wrapper is binary-compatible with hid_t (a single hid_t handle; member), so the call-site code is identical to using the raw integer ID — there is no per-call overhead.

Explicit mode (catch-conversions opt-in)

Compile with -DH5CPP_CONVERSION_EXPLICIT to require an explicit cast at every conversion site. Useful when migrating a large codebase from raw hid_t to h5cpp wrappers and you want the compiler to flag every mixed call:

// with H5CPP_CONVERSION_EXPLICIT defined:
h5::fd_t fd = h5::create("file.h5", H5F_ACC_TRUNC);
H5Dcreate(fd, ...); // ❌ compile error: no implicit conversion
H5Dcreate(static_cast<hid_t>(fd), ...); // ✔ explicit cast required
h5::ds_t ds(raw_hid_t); // ❌ compile error: explicit ctor
h5::ds_t ds = h5::ds_t{raw_hid_t}; // ✔ direct-initialisation form

Under the hood: H5CPP__EXPLICIT expands to explicit instead of empty (see H5Iall.hpp:16-20), qualifying both the from-CAPI constructor and the to-CAPI operator hid_t().

Reference-counting semantics

The conversion operators are non-owning views onto the same hid_t. The wrapper retains exclusive ownership; the conversion does NOT transfer responsibility:

OperationHDF5 refcountWhy
From-CAPI ctor: h5::fd_t fd(raw) incremented (via H5Iinc_ref) The raw hid_t keeps its existing owner. The wrapper takes a second reference so the destructor can independently call capi_close without double-freeing.
To-CAPI conversion: H5Dcreate(fd, ...) unchanged The wrapper retains ownership. The C API call sees the same hid_t value; when it returns, the wrapper still holds the only ref, and the destructor will close it.
Brace-init ctor: h5::fd_t{raw} unchanged Distinct from the from-CAPI ctor — used when the wrapper is taking ownership of a raw handle that h5cpp itself produced (e.g., the return value of H5Fcreate). No inc_ref because there's no existing C-side owner.
Copy ctor / copy-assign incremented Both copies refer to the same underlying HDF5 object; each independently closes on destruction.
Move ctor / move-assign unchanged; source invalidated Source handle set to H5I_UNINIT; destructor on the source is a no-op (H5Iis_valid check).
Destructor decremented (via capi_close) HDF5's close functions are refcount-aware: H5Fclose etc. decrement the ref and only release the underlying resource at refcount 0.

The "**DON'T**" pattern this protects against:

// DON'T: stash a hid_t copy of a temporary wrapper.
hid_t bad = h5::create("file.h5", H5F_ACC_TRUNC); // wrapper destructs immediately
H5Dcreate(bad, ...); // ❌ bad refers to a closed handle
// DO: capture the wrapper, then convert at call sites where you need it.
auto fd = h5::create("file.h5", H5F_ACC_TRUNC); // wrapper lives until block exit
H5Dcreate(fd, "ds", ...); // ✔ implicit conversion at the call site
H5Dcreate(static_cast<hid_t>(fd), "ds", ...); // ✔ explicit cast, same effect

(The full call-site discussion lives in h5cpp/H5Fcreate.hpp:14-49 — the docstring on h5::create walks through both the DO and DON'T forms.)

Async mode — conversion deleted at the type level

h5cpp's async surface (h5::async::create, h5::async::open, h5::async::* factories) returns specialised wrappers that deliberately disable both conversion directions:

// h5cpp/H5Iall.hpp:316-319
template <class T, capi_close_t capi_call>
using async_hid_t = detail::hid_t<T, capi_call, false, false, detail::hdf5::any>;
// h5cpp/H5Iall.hpp:354-357 — the h5::async::* aliases:
using fd_t = impl::async_hid_t<impl::fd_t, H5Fclose>;
using ds_t = impl::async_did_t<impl::ds_t, H5Dclose>;

The template parameters false, false (from_capi, to_capi) select a specialisation where operator hid_t() const = delete; is declared at the type level (see H5Iall.hpp:155). Any attempt to pass an async descriptor to a raw HDF5 call fails to compile with a clear "use of deleted function" diagnostic:

h5::async::fd_t afd = h5::async::create("file.h5", H5F_ACC_TRUNC);
H5Dcreate(afd, ...); // ❌ error: use of deleted function 'operator hid_t()'
// — async descriptors must route through the executor
h5::write(afd, "/ds", v); // ✔ correct: dispatched through h5::async overloads
h5::gr_t write(const LOC &parent, const std::string &path, const T &src)
Write a sparse matrix or vector as a CSC group.
Definition H5Dsparse.hpp:185

The intent: an async descriptor's value is only valid on the executor thread; passing it to a synchronous C API call would bypass the executor and access the handle from the wrong thread. The type-level deletion makes that class of bug impossible.

Internal code that legitimately needs the raw hid_t from an async wrapper reads handle directly (the protected: field is exposed to the executor implementation via friendship); user code goes through the typed h5::write / h5::read / h5::async::* overloads that detect the type via is_async_v<T> and dispatch through the FAPL-resolved executor.

The full list of async-mode wrappers — h5::async::fd_t, h5::async::ds_t, h5::async::gr_t, h5::async::at_t, h5::async::ob_t — is in Handles, Descriptors & Property Lists §Async-mode handles". @section conversion_inventory Which handles use which policy The <tt>h5::impl::detail::hid_t</tt> template's 3rd and 4th parameters (<tt>from_capi</tt>, <tt>to_capi</tt>) select the conversion policy. Across every public alias h5cpp ships there are only two combinations: <table class="markdownTable"> <tr><th>Policy</th><th>Template parameters</th><th>Covers</th></tr> <tr><td><b>Default — both directions enabled</b><br/> (governed by <tt>H5CPP__EXPLICIT</tt> as documented in @ref conversion_default and @ref conversion_explicit)</td> <td><tt>hid_t\<…, true, true, hdf5_class\></tt></td> <td>Every object handle (<tt>h5::fd_t</tt>, <tt>h5::ds_t</tt>, <tt>h5::gr_t</tt>, <tt>h5::at_t</tt>, <tt>h5::ob_t</tt>, <tt>h5::sp_t</tt>), the datatype template <tt>h5::dt_t\<T\></tt>, the reference handle <tt>h5::reference_t</tt>, and every property-list wrapper (<tt>h5::fapl_t</tt>, <tt>h5::fcpl_t</tt>, <tt>h5::dapl_t</tt>, <tt>h5::dcpl_t</tt>, <tt>h5::dxpl_t</tt>, <tt>h5::lcpl_t</tt>, <tt>h5::lapl_t</tt>, <tt>h5::gcpl_t</tt>, <tt>h5::gapl_t</tt>, <tt>h5::tcpl_t</tt>, <tt>h5::tapl_t</tt>, <tt>h5::acpl_t</tt>, <tt>h5::ocpl_t</tt>, <tt>h5::ocrl_t</tt>, <tt>h5::fmpl_t</tt>, <tt>h5::scpl_t</tt>).</td></tr> <tr><td><b>Async — both directions DELETED at type level</b><br/> (see @ref conversion_async)</td> <td><tt>hid_t\<…, false, false, hdf5_class\></tt></td> <td><tt>h5::async::fd_t</tt>, <tt>h5::async::ds_t</tt>, <tt>h5::async::gr_t</tt>, <tt>h5::async::at_t</tt>, <tt>h5::async::ob_t</tt></td></tr> </table> For the complete reference — every alias, the HDF5 close function it calls, its typical factory, plus the shape-descriptor types and the property-list defaults — see @ref link_handle_reference "Handles, Descriptors & Property Lists". The 5th template parameter (<tt>hdf5_class</tt>) selects a specialisation that adds class-specific members orthogonal to the conversion policy (e.g. the <tt>attribute</tt> specialisation carries the parent dataset's <tt>hid_t</tt> for <tt>ds[name]</tt> subscript access). It does not affect the conversion direction — see @ref handle_ref_implementation for the full parameter breakdown. @section conversion_use_cases When to override the defaults Three concrete scenarios for compiling with one of the opt-out macros: <table class="markdownTable">

Goal

Macro

Trade-off

Audit a large codebase for raw hid_t usage during a migration to h5cpp wrappers

-DH5CPP_CONVERSION_EXPLICIT

Every mixed call site now needs static_cast<hid_t>(...); the compiler flags any that don't have one. Once the audit is done, drop the macro to restore implicit conversions.

Forbid mixing h5cpp wrappers with the C API entirely (h5cpp-only codebase)

-DH5CPP_CONVERSION_TO_CAPI_DISABLED   (with H5CPP_CONVERSION_EXPLICIT)

The wrapper has no operator hid_t at all; passing a wrapper to a C API call fails to compile. Useful for enforcing a strict h5cpp surface.

Forbid wrapping raw hid_t values that originate outside h5cpp (e.g. third-party C bindings)

-DH5CPP_CONVERSION_FROM_CAPI_DISABLED

The wrapper has no from-CAPI constructor; only h5cpp factories can produce wrappers. Useful when integrating with a foreign HDF5 layer that has its own lifetime tracking.

See also
h5::fd_t h5::ds_t h5::gr_t h5::at_t RAII Handles Handles, Descriptors & Property Lists Error Handling