With easy to use Pythonic syntax:

The following function call provides similar, easy to use syntactic sugar we find in Python: arguments may be specified in arbitrary order... The actual functionality is provided by `libhdf5` H5CPP simplifies the otherwise complex HDF5 C library usage

h5::ds_t h5::create( const h5::fd_t& fd, const std::string& dataset_name,
    h5::current_dims{n,m,...},
    h5::max_dims{i,k, ..}, // j > n, k > m ... or H5S_UNLIMITED
    h5::chunk{1,4,5} | h5::deflate{4} | h5::compact | h5::dont_filter_partial_chunks
        | h5::fill_value{STR} | h5::fill_time_never | h5::alloc_time_early 
        | h5::fletcher32 | h5::shuffle | h5::nbit,
    h5::chunk_cache{...} | h5::all_coll_metadata_ops{...} | ... 
);
  • array length computed at compile time
  • optional, daisy chained flags for fine tuning
  • additional properties to tweak knobs and levers
  • argument may be in arbitrary order

Learn what you need now, but have ample of room to refine/perfect later...

An EXAMPLE for event recording pipeline

#include <h5cpp/core>
    #include "generated.h"
#include <h5cpp/io>
int main(){
    h5::fd_t fd = h5::create("some-file.h5",H5F_ACC_TRUNC);
    ...
    try {
        h5::pt_t pt = h5::create<sn::example::complicated_struct_t>(fd,
            "/internal/path/to/dataset", h5::max_dims{H5S_UNLIMITED}, h5::chunk{1024} );
        sn::example::complicated_struct_t event;
        for(size_t i=0; i<size; i++ )
            h5::append(pt, event);
    } catch ( const h5::error::any& e ){ ... }
}
  • we are to record a sequence of events
  • with some complicated layout
  • into an HDF5 container
  • and dataset within
  • with good IO properties
  • making sure things don't go wild...

The actual introspection

#include <h5cpp/core>
  #include "generated.h"
#include <h5cpp/io>
int main(){
    h5::fd_t fd = h5::create("lightning-talk-example.h5",H5F_ACC_TRUNC);
    h5::pt_t pt = h5::create(fd, "stream",
        h5::max_dims{H5S_UNLIMITED}, h5::chunk{1024} );
    ...
    try {
        h5::pt_t pt = h5::create<sn::example::complicated_struct_t>(fd,
            "/inernal/path/to/dataset", h5::max_dims{H5S_UNLIMITED}, h5::chunk{1024} );
        sn::example::complicated_struct_t event;
        for(size_t i=0; i<size; i++ )
            h5::append(pt, event);
    } catch ( const h5::error::any& e ){ ... }
}
  • notice the included 'generated.h'
  • as the name suggest: generated.h doesn't exist yet...

A header file with HDF5 Compound Type descriptors:

#ifndef H5CPP_GUARD_ErRrk
#define H5CPP_GUARD_ErRrk
namespace h5{
    template<> hid_t inline register_struct(){
        hsize_t at_00_[] ={7};            hid_t at_00 = H5Tarray_create(H5T_NATIVE_FLOAT,1,at_00_);
        hsize_t at_01_[] ={3};            hid_t at_01 = H5Tarray_create(H5T_NATIVE_DOUBLE,1,at_01_);
        hid_t ct_00 = H5Tcreate(H5T_COMPOUND, sizeof (sn::typecheck::Record));
        H5Tinsert(ct_00, "_char",	HOFFSET(sn::typecheck::Record,_char),H5T_NATIVE_CHAR);
		...
		H5Tclose(at_03); H5Tclose(at_04); H5Tclose(at_05); 
        return ct_02;
    };
}
H5CPP_REGISTER_STRUCT(sn::example::Record);
#endif
  • random include guards
  • within namespace
  • template specialization for h5::operators
  • HDF5 compound types are recursively created
  • calls the template specialization when h5::operator needs it
# How far does it go in terms of complexity?

Fairly complex POD struct

namespace typecheck {
    struct struct_t { /*the types with direct mapping to HDF5*/
        char  _char; unsigned char _uchar; short _short; unsigned short _ushort; int _int; unsigned int _uint;
        long _long; unsigned long _ulong; long long int _llong; unsigned long long _ullong;
        float _float; double _double; long double _ldouble;
        bool _bool;
        // wide characters are not supported in HDF5
        // wchar_t _wchar; char16_t _wchar16; char32_t _wchar32;
    };
}
namespace other {
    struct struct_t {                    // POD struct with nested namespace
        type_alias_t                idx; // typedef type 
        double              field_02[3]; // const array mapped 
        typecheck::struct_t field_03[4]; //
    };
}
namespace example {
    struct complicated_struct_t {        // POD struct with nested namespace
        type_alias_t                idx; // typedef type 
        float                 array[7];  // array of elementary types
        sn::other::struct_t field_04[5]; // embedded struct 1D array
        other::struct_t  field_05[3][8]; // array of arrays 
    };
}

Comma Separated Values to HDF5

#include "csv.h"
#include "struct.h"
#include <h5cpp/core>      // has handle + type descriptors
    #include "generated.h" // uses type descriptors
#include <h5cpp/io>        // uses generated.h + core 

int main(){
    h5::fd_t fd = h5::create("output.h5",H5F_ACC_TRUNC);
    h5::ds_t ds = h5::create<input_t>(fd,  "simple approach/dataset.csv",
                    h5::max_dims{H5S_UNLIMITED}, h5::chunk{10} | h5::gzip{9} );
    h5::pt_t pt = ds;
    ds["data set"] = "monroe-county-crash-data2003-to-2015.csv";
    ds["cvs parser"] = "https://github.com/ben-strasser/fast-cpp-csv-parser";

    constexpr unsigned N_COLS = 5;
    io::CSVReader<N_COLS> in("input.csv"); // number of cols may be less, than total columns in a row, we're to read only 5
    in.read_header(io::ignore_extra_column, "Master Record Number", "Hour", "Reported_Location","Latitude","Longitude");
    input_t row;                           // buffer to read line by line
    char* ptr;      // indirection, as `read_row` doesn't take array directly
    while(in.read_row(row.MasterRecordNumber, row.Hour, ptr, row.Latitude, row.Longitude)){
        strncpy(row.ReportedLocation, ptr, STR_ARRAY_SIZE); // defined in struct.h
        h5::append(pt, row);
    }
}
    
  • CSV header only library by Ben Strasser, and a type definition for the record
  • h5cpp includes
  • translation unit, the program
  • create HDF5 container, and dataset
  • decorate it with attributes
  • do I/O operations within a loop

Speaking of Attributes

Here are some examples... notice the visually appealing bracket operators:

h5::ds_t ds = h5::write(fd,"some dataset with attributes", ... );
        ds["att_01"] = 42 ;
        ds["att_02"] = {1.,3.,4.,5.};
        ds["att_03"] = {'1','3','4','5'};
        ds["att_04"] = {"alpha", "beta","gamma","..."};
        ds["att_05"] = "const char[N]";
        ds["att_06"] = u8"const char[N]áééé";
        ds["att_07"] = std::string( "std::string");
        ds["att_08"] = record; // pod/compound datatype
        ds["att_09"] = vector; // vector of pod/compound type
        ds["att_10"] = matrix; // linear algebra object
        
  • obtain a handle by h5::create | h5::open | h5::write
  • rank N objects, even compound types when h5cpp compiler used
  • arrays of various element types
  • mapped to rank 0 variable length character types
luckily
there is detailed H5CPP
documentation to help you!!!
alternatively you I solve interesting problems on HDF User Group mailing list

Ability to add Custom Types

struct float16 {
    short value;
};
template <> h5::dt_t<float16> create() {
    h5::dt_t<float16> tid{H5Tcopy( H5T_NATIVE_FLOAT )};
    H5Tset_fields(tid, 15, 10, 5, 0, 10);
    H5Tset_precision(tid, 16);
    H5Tset_ebias(tid, 15);
    H5Tset_size(tid,2);
    return tid;
};
  • you need the c++ type definition
  • template specialization
  • HDF5 type description