Skip to content

Commit 0504008

Browse files
committed
[#282]:svarga:docs, decorate H5A public surface + add bracket-syntax read/write for gr_t/ob_t
Worked example for the alias-first docstring vocabulary across the full H5A entry-point surface (h5::create attribute overload, h5::open, h5::aread, h5::awrite, h5::adelete), plus a symmetric bracket-syntax read/write API extending the existing ds_t["x"] = v idiom to gr_t and ob_t and adding implicit-conversion read. DOCSTRINGS * H5Acreate.hpp / H5Aopen.hpp / H5Aread.hpp / H5Awrite.hpp / H5Adelete.hpp -- each public entry point gets the standard alias-first docstring template: - \func_attr_hdr (resolves to @InGroup attribute-io) - \tpar_T for element-type template parameter - \par_args_attr for variadic context-sensitive optionals (h5::current_dims, h5::acpl_t) -- attribute-specific surface, not the dataset \par_args - \par_current_dims where relevant - explicit @param for parent (notes ::hid_t / gr_t / ds_t / ob_t / dt_t<T>; h5::fd_t needs explicit static_cast<::hid_t>(fd)) - @return spelling out h5::at_t RAII handle + H5Aclose lifecycle - @throws for the leaf exception types - @code example block showing the function in context - \sa_h5cpp / \sa_hdf5 cross-reference bundles * Examples across the three primary ops (create / awrite / aread) share the same schema_version / spacing / label triple so a reader can follow the round-trip; each example is verbatim-compilable. SFINAE-RETURN-TYPE MACROS * One per entry point, gated on H5CPP_DOXYGEN: - H5CPP_ATTR_CREATE_RET (existing) - H5CPP_ATTR_OPEN_RET / H5CPP_ATTR_DELETE_RET / H5CPP_ATTR_READ_RET / H5CPP_ATTR_WRITE_RET Real compile resolves through std::enable_if_t / attr_parent_t; Doxygen pass collapses to the advertised return type so the rendered signature reads as plain h5::at_t / void / T instead of leaking the SFINAE machinery into the headline. * doxy/Doxyfile EXPAND_AS_DEFINED += each macro. TEMPLATE PARAMETER RENAME * HID_T / P template parameter renamed to lowercase hid_t across every overload for symmetry with the rendered signature presentation (matches the user's earlier preference on H5Acreate). * All in-body uses of the HDF5 typedef are now qualified as ::hid_t to disambiguate from the shadowed template parameter (38 sites in H5Aread.hpp, ~60 sites in H5Awrite.hpp). BRACKET-SYNTAX API (gr_t["x"] / ob_t["x"] + symmetric implicit read) * H5Iall.hpp: - operator[](const char[]) declarations added to the hdf5::any spec (covers ob_t) and the hdf5::attribute spec (covers gr_t). The existing declaration on the hdf5::dataset spec stays for ds_t. Doxygen-visible docstrings on each. - Symmetric implicit read declaration added to the attribute spec: template <class V, class = std::enable_if_t<!std::is_same_v<V, ::hid_t>>> operator V() const; SFINAE excludes ::hid_t so static_cast<::hid_t> on the handle itself still resolves to the inherited base-spec accessor. * H5Awrite.hpp: - operator[] specializations for h5::gr_t and h5::ob_t mirroring the existing h5::ds_t body (open if H5Aexists, otherwise UNINIT at_t with ds + name fields populated). * H5Aread.hpp: - Implementation of h5::at_t::operator V() const forwards to h5::aread<V>(this->ds, this->name) using the raw ::hid_t directly. Crucial: does NOT wrap in h5::ob_t{this->ds} -- doing so would close the underlying handle when the temporary destructs and orphan the caller's parent (caught by end-to-end test, see commit history). UMBRELLA HEADER FIX * h5cpp/io: added missing #include "H5Adelete.hpp". Pre-existing bug -- adelete was reachable only via direct header include, not through the public h5cpp/all umbrella. DOCS * doxy/anchors.dox -- new handle_ref_indexer subsection on the Handle Reference page describing the bracket syntax, the at_t-as- intermediate model, the read-T-from-LHS pattern, and the caveats (auto deduces at_t, not the value; parent lifetime must outlive the conversion). VERIFICATION * End-to-end compile + run on each docstring example individually and on the bracket-syntax round-trip (write 4 attrs through gr_t[]/ob_t[]/explicit awrite, read 4 back through implicit conversion + explicit aread, h5dump confirms all expected on-disk dtypes and shapes). * Doxygen build clean -- no warnings touching H5A files or H5Iall.
1 parent 2cb78c5 commit 0504008

10 files changed

Lines changed: 555 additions & 144 deletions

File tree

doxy/Doxyfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,7 @@ MACRO_EXPANSION = YES
168168
EXPAND_ONLY_PREDEF = YES
169169
PREDEFINED += H5CPP_DOXYGEN=1
170170
EXPAND_AS_DEFINED += H5CPP_ATTR_CREATE_RET
171+
EXPAND_AS_DEFINED += H5CPP_ATTR_OPEN_RET
172+
EXPAND_AS_DEFINED += H5CPP_ATTR_DELETE_RET
173+
EXPAND_AS_DEFINED += H5CPP_ATTR_READ_RET
174+
EXPAND_AS_DEFINED += H5CPP_ATTR_WRITE_RET

doxy/anchors.dox

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,41 @@
554554
* <tr><td>`h5::dt_t<T>` (template)</td><td>`H5Tclose`</td><td>datatype for `T`</td> <td>default-constructed: `h5::dt_t<int>{}` looks up the cached HDF5 type for `T`</td></tr>
555555
* </table>
556556
*
557+
* @subsection handle_ref_indexer Attribute bracket syntax
558+
*
559+
* `h5::ds_t`, `h5::gr_t`, and `h5::ob_t` expose an `operator[](const char*)`
560+
* that returns a transient `h5::at_t` carrying the parent handle and the
561+
* attribute name. Combined with `at_t::operator=(V)` and the templated
562+
* `at_t::operator V() const`, this gives a symmetric Python-dict-like API
563+
* for attribute I/O without leaving expression syntax:
564+
*
565+
* @code
566+
* h5::gr_t gr = h5::gopen(fd, "/grid");
567+
*
568+
* // write
569+
* gr["title"] = std::string{"hello"};
570+
* gr["count"] = 42;
571+
* gr["values"] = std::vector<double>{1.0, 2.0, 3.0};
572+
*
573+
* // read — implicit conversion picks T from the left-hand side
574+
* std::string title = gr["title"];
575+
* int count = gr["count"];
576+
* @endcode
577+
*
578+
* The bracket form is sugar over the free-function surface; both write the
579+
* same on-disk bytes as `h5::awrite(gr, "title", ...)` and read the same
580+
* value as `h5::aread<T>(gr, "title")`. Type `V` follows the same dispatch
581+
* matrix as the rest of the I/O API (see @ref link_base_template_types
582+
* "Supported Types"). The conversion excludes `::hid_t` so plain
583+
* `static_cast<::hid_t>(at_t)` still resolves to the inherited handle accessor.
584+
*
585+
* Caveats:
586+
* - `auto v = gr["x"]` deduces `at_t`, **not** the attribute value; the
587+
* implicit conversion needs a concrete LHS type to fire.
588+
* - The transient `at_t` only carries `ds` + `name`; if the parent handle
589+
* is closed before the conversion runs, the read throws
590+
* `h5::error::io::attribute::read`.
591+
*
557592
* @section handle_ref_async Async-mode handles
558593
*
559594
* Specialisations with both conversion directions deleted at the type

h5cpp/H5Acreate.hpp

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,111 @@
1010
#include "H5Sall.hpp"
1111
#include "H5capi.hpp"
1212

13+
// Return-type macro for h5::create<T>(parent, path, ...).
14+
// Real compile: the SFINAE-constrained h5::impl::attr_parent_t<hid_t>
15+
// alias — only parents that can carry attributes (file, group,
16+
// dataset, opaque object, committed datatype) participate in overload
17+
// resolution.
18+
// Doxygen : collapses to plain h5::at_t so the rendered signature
19+
// reads cleanly without leaking the std::enable_if_t<...> machinery
20+
// into the headline. Gated on H5CPP_DOXYGEN from doxy/Doxyfile.
21+
#ifdef H5CPP_DOXYGEN
22+
# define H5CPP_ATTR_CREATE_RET(hid_t) h5::at_t
23+
#else
24+
# define H5CPP_ATTR_CREATE_RET(hid_t) h5::impl::attr_parent_t<hid_t>
25+
#endif
1326

1427
namespace h5 {
1528
namespace impl {
16-
/*this template defines what HDF5 object types may have attributes */
1729
template <class H, class T=void> using is_valid_attr =
1830
std::bool_constant<std::is_same_v<H, ::hid_t> ||
1931
std::is_same_v<H, h5::gr_t> || std::is_same_v<H, h5::ds_t> ||
2032
std::is_same_v<H, h5::ob_t> || std::is_same_v<H, h5::dt_t<T>>>;
2133
/*template <class H, class T=void> using is_valid_attr =
2234
std::bool_constant<std::is_same_v<H, h5::ds_t>>;*/
35+
36+
template <class hid_t>
37+
using attr_parent_t = std::enable_if_t<is_valid_attr<hid_t>::value, h5::at_t>;
2338
}
2439

25-
template<class T, class HID_T, class... args_t>
26-
inline std::enable_if_t<h5::impl::is_valid_attr<HID_T>::value,
27-
h5::at_t> create( const HID_T& parent, const std::string& path, args_t&&... args ){
40+
/**
41+
* \func_attr_hdr
42+
* @brief Create a new attribute of element type `T` on a parent HDF5 object.
43+
*
44+
* Attributes are small named metadata items attached to a parent object (file, group, dataset, opaque object, or committed datatype).
45+
* `h5::create` allocates the on-disk slot — the value itself is later deposited with `h5::awrite(parent, path, value)` or read back with
46+
* `h5::aread<T>(parent, path)`. The element type `T` follows the same dispatch as the dataset API (see @ref link_base_template_types
47+
* "Supported Types"): elementary scalar, registered compound, fixed or variable-length string, etc. Attributes do not chunk and do not
48+
* support partial I/O, so neither `h5::chunk{}` nor offset/stride/count apply here.
49+
*
50+
* @param parent open parent handle: raw `::hid_t`, `h5::gr_t`, `h5::ds_t`, `h5::ob_t`, or `h5::dt_t<T>` — enforced at compile
51+
* time via `h5::impl::is_valid_attr`. Note: typed `h5::fd_t` is **not** accepted directly; pass `static_cast<::hid_t>(fd)`
52+
* to attach an attribute to the file root.
53+
* @param path attribute name (UTF-8); resolved relative to `parent`.
54+
* \par_args_attr
55+
* \tpar_T
56+
* @tparam hid_t deduced from the `parent` argument; must satisfy `h5::impl::is_valid_attr<hid_t>::value`.
57+
* @return `h5::at_t` RAII handle owning the new attribute id; closes
58+
* automatically via `H5Aclose` on scope exit. Throws on failure.
59+
*
60+
* The optional arguments are context-sensitive and may be passed in any order. By default the attribute is created with an empty
61+
* (rank-0) shape and the default attribute creation property list:
62+
*
63+
* \par_current_dims
64+
* Defaults to `{0}` — a scalar attribute. Pass a non-trivial extent for an array-valued attribute (e.g. `h5::current_dims{8}` for an
65+
* 8-element vector attribute).
66+
*
67+
* @param acpl attribute creation property list (`h5::acpl_t`); defaults to `H5P_ATTRIBUTE_CREATE`.
68+
*
69+
* @throws h5::error::io::attribute::create on `H5Acreate2` failure (parent does not exist, attribute already exists, type
70+
* conversion error, etc.).
71+
* @throws h5::error::property_list::misc when an invalid acpl is supplied.
72+
*
73+
* <br/><b>example:</b> explicit pre-allocation — useful when the shape
74+
* is non-default or you want the attribute slot to exist before any
75+
* value is written. Most call sites skip `h5::create<T>` and let
76+
* `h5::awrite` create-on-demand instead.
77+
* @code
78+
* h5::fd_t fd = h5::open("file.h5", H5F_ACC_RDWR);
79+
* h5::ds_t ds = h5::open(fd, "/grid/data");
80+
*
81+
* // rank-1 of 3 floats; deposit the value later with h5::awrite.
82+
* h5::create<float>(ds, "spacing", h5::current_dims{3});
83+
*
84+
* // Scalar attribute on the file root — fd_t needs an explicit cast
85+
* // (h5::fd_t is not in is_valid_attr; raw ::hid_t is).
86+
* h5::create<double>(static_cast<::hid_t>(fd), "schema_version");
87+
*
88+
* // Round-trip with h5::awrite / h5::aread:
89+
* h5::awrite(ds, "spacing", std::vector<float>{0.5f, 0.5f, 1.0f});
90+
* auto v = h5::aread<std::vector<float>>(ds, "spacing");
91+
* @endcode
92+
*
93+
* \sa_h5cpp
94+
* \sa_hdf5
95+
* @sa h5::aread h5::awrite @ref link_handle_reference
96+
* "Handles, Descriptors, and Property Lists"
97+
*/
98+
template<class T, class hid_t, class... args_t>
99+
inline H5CPP_ATTR_CREATE_RET(hid_t)
100+
create( const hid_t& parent, const std::string& path, args_t&&... args ){
28101
try {
29102
h5::acpl_t default_acpl{ H5Pcreate(H5P_ATTRIBUTE_CREATE) };
30103
const h5::acpl_t& acpl = arg::get(default_acpl, args...);
31104

32105
H5CPP_CHECK_PROP( acpl, h5::error::property_list::misc, "invalid attribute create property" );
33106

34107
// and dimensions
35-
h5::current_dims_t current_dims_default{0}; // if no current dims_present
108+
h5::current_dims_t current_dims_default{0}; // if no current dims_present
36109
// this mutable value will be referenced
37110
const h5::current_dims_t& current_dims = arg::get(current_dims_default, args...);
38111
// no partial IO or chunks
39112
h5::sp_t space = h5::create_simple( current_dims );
40113
using element_t = typename meta::decay<T>::type;
41114
h5::dt_t<element_t> type;
42-
hid_t id = H5I_UNINIT;
43-
H5CPP_CHECK_NZ( (id = H5Acreate2( static_cast<hid_t>( parent ), path.c_str(),
44-
static_cast<hid_t>(type), static_cast<hid_t>( space ), static_cast<hid_t>( acpl ), H5P_DEFAULT )),
115+
::hid_t id = H5I_UNINIT;
116+
H5CPP_CHECK_NZ( (id = H5Acreate2( static_cast<::hid_t>( parent ), path.c_str(),
117+
static_cast<::hid_t>(type), static_cast<::hid_t>( space ), static_cast<::hid_t>( acpl ), H5P_DEFAULT )),
45118
h5::error::io::attribute::create, "couldn't create attribute");
46119
return h5::at_t{id};
47120
} catch( const std::runtime_error& err ) {

h5cpp/H5Adelete.hpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,47 @@
77
#include <string>
88
#include <type_traits>
99

10+
// Return-type macro for h5::adelete(parent, name). See H5Acreate.hpp.
11+
#ifdef H5CPP_DOXYGEN
12+
# define H5CPP_ATTR_DELETE_RET(hid_t) void
13+
#else
14+
# define H5CPP_ATTR_DELETE_RET(hid_t) std::enable_if_t<h5::impl::is_valid_attr<hid_t>::value, void>
15+
#endif
16+
1017
namespace h5 {
11-
// Delete an attribute from an HDF5 object by name
12-
template<class HID_T>
13-
inline std::enable_if_t<h5::impl::is_valid_attr<HID_T>::value, void>
14-
adelete(const HID_T& parent, const std::string& name) {
18+
/**
19+
* \func_attr_hdr
20+
* @brief Delete an attribute by name from a parent HDF5 object.
21+
*
22+
* Removes the named attribute from the on-disk representation of
23+
* `parent`. The operation is final — there is no rollback once
24+
* `H5Adelete` has succeeded.
25+
*
26+
* @param parent open parent handle: raw `::hid_t`, `h5::gr_t`, `h5::ds_t`, `h5::ob_t`, or `h5::dt_t<T>` — enforced at compile
27+
* time via `h5::impl::is_valid_attr`. Typed `h5::fd_t` is **not** accepted directly; pass `static_cast<::hid_t>(fd)`.
28+
* @param name attribute name (UTF-8); resolved relative to `parent`.
29+
*
30+
* @tparam hid_t deduced from the `parent` argument; must satisfy `h5::impl::is_valid_attr<hid_t>::value`.
31+
* @return void — failures are reported by throwing.
32+
*
33+
* @throws h5::error::io::attribute::delete_ on `H5Adelete` failure (attribute does not exist, parent invalid, write access denied).
34+
*
35+
* <br/><b>example:</b>
36+
* @code
37+
* h5::ds_t ds = h5::open(fd, "/grid/data");
38+
* h5::adelete(ds, "stale_marker");
39+
* @endcode
40+
*
41+
* \sa_h5cpp
42+
* \sa_hdf5
43+
* @sa h5::create h5::open @ref link_handle_reference
44+
* "Handles, Descriptors, and Property Lists"
45+
*/
46+
template<class hid_t>
47+
inline H5CPP_ATTR_DELETE_RET(hid_t)
48+
adelete(const hid_t& parent, const std::string& name) {
1549
H5CPP_CHECK_NZ(
16-
H5Adelete(static_cast<hid_t>(parent), name.c_str()),
50+
H5Adelete(static_cast<::hid_t>(parent), name.c_str()),
1751
h5::error::io::attribute::delete_,
1852
"couldn't delete attribute");
1953
}

h5cpp/H5Aopen.hpp

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,59 @@
66
#include "H5Acreate.hpp"
77
#include <string>
88
#include <type_traits>
9+
10+
// Return-type macro for h5::open(parent, path, acpl) attribute overload.
11+
// Real compile: SFINAE-constrained h5::impl::attr_parent_t<hid_t> alias.
12+
// Doxygen : plain h5::at_t. See H5Acreate.hpp for the rationale.
13+
#ifdef H5CPP_DOXYGEN
14+
# define H5CPP_ATTR_OPEN_RET(hid_t) h5::at_t
15+
#else
16+
# define H5CPP_ATTR_OPEN_RET(hid_t) h5::impl::attr_parent_t<hid_t>
17+
#endif
18+
919
namespace h5 {
10-
template<class HID_T>
11-
inline std::enable_if_t<h5::impl::is_valid_attr<HID_T>::value,
12-
h5::at_t> open(const HID_T& parent, const std::string& path, const h5::acpl_t& acpl = h5::default_acpl ){
20+
/**
21+
* \func_attr_hdr
22+
* @brief Open an existing attribute by name on a parent HDF5 object.
23+
*
24+
* Lookup is by attribute name relative to `parent`. The returned
25+
* `h5::at_t` is RAII-managed; the underlying CAPI handle is closed
26+
* via `H5Aclose` on scope exit. Use @ref h5::aread to retrieve the
27+
* value, or pass the handle to `h5::awrite` overloads that take an
28+
* already-open attribute.
29+
*
30+
* @param parent open parent handle: raw `::hid_t`, `h5::gr_t`, `h5::ds_t`, `h5::ob_t`, or `h5::dt_t<T>` — enforced at compile
31+
* time via `h5::impl::is_valid_attr`. Typed `h5::fd_t` is **not** accepted directly; pass `static_cast<::hid_t>(fd)`.
32+
* @param path attribute name (UTF-8); resolved relative to `parent`.
33+
* @param acpl attribute creation property list (`h5::acpl_t`); defaults to `h5::default_acpl`.
34+
*
35+
* @tparam hid_t deduced from the `parent` argument; must satisfy `h5::impl::is_valid_attr<hid_t>::value`.
36+
* @return `h5::at_t` RAII handle owning the opened attribute id; closes automatically via `H5Aclose` on scope exit. Throws on failure.
37+
*
38+
* @throws h5::error::io::attribute::open on `H5Aopen` failure (attribute
39+
* not present, parent does not exist, invalid acpl).
40+
*
41+
* <br/><b>example:</b>
42+
* @code
43+
* h5::fd_t fd = h5::open("file.h5", H5F_ACC_RDONLY);
44+
* h5::ds_t ds = h5::open(fd, "/grid/data");
45+
* h5::at_t att = h5::open(ds, "spacing");
46+
* auto axes = h5::aread<std::vector<float>>(ds, "spacing");
47+
* @endcode
48+
*
49+
* \sa_h5cpp
50+
* \sa_hdf5
51+
* @sa h5::aread h5::awrite @ref link_handle_reference
52+
* "Handles, Descriptors, and Property Lists"
53+
*/
54+
template<class hid_t>
55+
inline H5CPP_ATTR_OPEN_RET(hid_t)
56+
open(const hid_t& parent, const std::string& path, const h5::acpl_t& acpl = h5::default_acpl ){
1357

1458
H5CPP_CHECK_PROP( acpl, h5::error::io::attribute::open, "invalid attribute creation property" );
15-
hid_t attr = H5I_UNINIT;
16-
H5CPP_CHECK_NZ(( attr = H5Aopen( static_cast<hid_t>(parent),
17-
path.c_str(), static_cast<hid_t>(acpl))), h5::error::io::attribute::open, "can't open attribute..." );
59+
::hid_t attr = H5I_UNINIT;
60+
H5CPP_CHECK_NZ(( attr = H5Aopen( static_cast<::hid_t>(parent),
61+
path.c_str(), static_cast<::hid_t>(acpl))), h5::error::io::attribute::open, "can't open attribute..." );
1862
return h5::at_t{attr};
1963
}
2064
}

0 commit comments

Comments
 (0)