typedef unsigned long long int MyUInt;
namespace sn {
namespace example {
struct Record {
MyUInt field_01;
char field_02;
double field_03[3];
other::Record field_04[4];
};
}
}
write your cpp program as if `generated.h` were already written #include "some_header_file.h" #include <h5cpp/core> #include "generated.h" #include <h5cpp/io> int main(){ std::vector<sn::example::Record> stream = ... h5::fd_t fd = h5::create("example.h5",H5F_ACC_TRUNC); h5::pt_t pt = h5::create<sn::example::Record>( fd, "stream of struct", h5::max_dims{H5S_UNLIMITED,7}, h5::chunk{4,7} | h5::gzip{9} ); ... }
h5::create | h5::write | h5::read | h5::append | h5::acreate | h5::awrite | h5::aread
#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
do the right thing. Here are some examples, and come with an easy to use operator:
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
H5CPP with parallel HDF5 #include <mpi.h> #include <h5cpp/all> ... int main(int argc, char** argv) { ... MPI_Init(NULL, NULL); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); ... { .. /* for H5CPP see next slide */.. } ... MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); }
H5CPP with parallel HDF5 { std::vector
v(nrows); auto fd = h5::create("hdf5_container_name.h5", H5F_ACC_TRUNC, h5::fcpl, h5::mpiio({mpi_comm, mpi_info}) ); h5::write( fd, "dataset", v, h5::chunk{nrows,1}, h5::current_dims{nrows,size},h5::count{nrows,1} h5::offset{0,rank}, h5::block{1,1}, h5::stride{1,1}, h5::collective ); }
#include <some_header_files>
int main(int argc, char *argv[]) {
sn::some_type_t object;
h5::write( somewhere, object, ... );
...
for( size_t i=0; i<huge_number; i+=batch_size)
h5::read( somewhere, object, ...);
}
std::is_compound
!= heterogeniousint[N][...]
std::vector<int>().data()
points to a contiguous memory, but when T = std::string
it doesn'tC++ Type trait based approach may not be the best way to go...
//a vector of pod struct
struct coo_t {
size_t row;
size_t column;
double value;
};
std::vector<coo_t> sparse_matrix;
// each field of the struct is a vector
struct csc_t {
std::vector<size_t> rowind; // row indices
std::vector<size_t> colptr; // start of new columns
std::vector<double> values; // nonzero values
};
csc_t sparse_matrix;
...
StatementMatcher h5templateMatcher = callExpr( allOf(
hasDescendant( declRefExpr( to( varDecl().bind("variableDecl") ) ) ),
hasDescendant( declRefExpr( to(
functionDecl( allOf(
eachOf(
hasName("h5::write"), hasName("h5::create"), hasName("h5::read"),
hasName("h5::append"),
hasName("h5::awrite"), hasName("h5::acreate"), hasName("h5::aread")
),
... ));
It is possible to identify if a container is STL like, provides direct access to its contiguous storage -- as std::vector<T>
does, or alternatively iterators for scatter/gather operations
template <typename T> using value_type_f = typename T::value_type;
template <typename T> using data_f = decltype(std::declval <T>().data());
template <typename T> using size_f = decltype(std::declval <T>().size());
template <typename T> using begin_f = decltype(std::declval <T>().begin());
template <typename T> using end_f = decltype(std::declval <T>().end());
template <typename T> using cbegin_f = decltype(std::declval <T>().cbegin());
template <typename T> using cend_f = decltype(std::declval <T>().cend());
template <typename T> using value = compat::detected_or <T, value_type_f, T>;
template <typename T> using has_value_type = compat::is_detected <value_type_f, T>;
template <typename T> using has_data = compat::is_detected <data_f, T>;
template <typename T> using has_direct_access = compat::is_detected <data_f, T>;
template <typename T> using has_size = compat::is_detected <size_f, T>;
template <typename T> using has_begin = compat::is_detected <begin_f, T>;
template <typename T> using has_end = compat::is_detected <end_f, T>;
template <typename T> using has_cbegin = compat::is_detected <cbegin_f, T>;
template <typename T> using has_cend = compat::is_detected <cend_f, T>;
template <typename T> using has_iterator = std::integral_constant <bool, has_begin <T>::value && has_end <T>::value >;
template <typename T> using has_const_iterator = std::integral_constant <bool, has_cbegin <T>::value && has_cend <T>::value >;
are dedicated category, as they all must provide mechanism to pass/receive data to/from some BLAS system call, however the naming varies from system to system.
The differences can be mitigated with a combination of
library | direct access | vector size |
---|---|---|
arma | memptr() | n_elem |
eigen | data() | size() |
blaze | data() | n/a |
blitz | data() | size() |
itpp | _data() | length() |
ublas | data().begin() | n/a |
dlib | (0,0) | size() |