Skip to content

Commit

Permalink
No constexpr rank via constexpr min-max rank. (#938)
Browse files Browse the repository at this point in the history
The commit removes the requirement for a constexpr rank. Instead
containers need a minimum and maximum constexpr rank.

The difficulties are:

  * Empty arrays can't figure out their runtime rank. This is an issue
    when deducing the dimension when writing to disk. The "solution" is
    to deduce with lowest rank possible.
  • Loading branch information
1uc committed May 14, 2024
1 parent 1ba7102 commit b3f34bd
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 54 deletions.
15 changes: 9 additions & 6 deletions include/highfive/bits/H5Attribute_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "../H5DataSpace.hpp"
#include "H5Converter_misc.hpp"
#include "H5Inspector_misc.hpp"
#include "H5ReadWrite_misc.hpp"
#include "H5Utils.hpp"
#include "h5a_wrapper.hpp"
Expand Down Expand Up @@ -73,10 +74,11 @@ inline void Attribute::read(T& array) const {
[this]() -> std::string { return this->getName(); },
details::BufferInfo<T>::Operation::read);

if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to read Attribute of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions " << buffer_info.n_dimensions;
ss << "Impossible to read attribute of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto dims = mem_space.getDimensions();
Expand Down Expand Up @@ -137,10 +139,11 @@ inline void Attribute::write(const T& buffer) {
[this]() -> std::string { return this->getName(); },
details::BufferInfo<T>::Operation::write);

if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to write buffer of dimensions " << buffer_info.n_dimensions
<< " into dataset of dimensions " << mem_space.getNumberDimensions();
ss << "Impossible to write attribute of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto w = details::data_converter::serialize<T>(buffer, dims, file_datatype);
Expand Down
6 changes: 3 additions & 3 deletions include/highfive/bits/H5DataType_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ inline EnumType<details::Boolean> create_enum_boolean() {
// Other cases not supported. Fail early with a user message
template <typename T>
AtomicType<T>::AtomicType() {
static_assert(details::inspector<T>::recursive_ndim == 0,
"Atomic types cant be arrays, except for char[] (fixed-length strings)");
static_assert(details::inspector<T>::recursive_ndim > 0, "Type not supported");
static_assert(
true,
"Missing specialization of AtomicType<T>. Therefore, type T is not supported by HighFive.");
}


Expand Down
7 changes: 4 additions & 3 deletions include/highfive/bits/H5Dataspace_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ inline DataSpace DataSpace::FromCharArrayStrings(const char (&)[N][Width]) {

namespace details {

/// dimension checks @internal
inline bool checkDimensions(const DataSpace& mem_space, size_t n_dim_requested) {
return checkDimensions(mem_space.getDimensions(), n_dim_requested);
inline bool checkDimensions(const DataSpace& mem_space,
size_t min_dim_requested,
size_t max_dim_requested) {
return checkDimensions(mem_space.getDimensions(), min_dim_requested, max_dim_requested);
}

} // namespace details
Expand Down
84 changes: 63 additions & 21 deletions include/highfive/bits/H5Inspector_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@
namespace HighFive {
namespace details {

inline bool checkDimensions(const std::vector<size_t>& dims, size_t n_dim_requested) {
if (dims.size() == n_dim_requested) {
inline bool checkDimensions(const std::vector<size_t>& dims,
size_t min_dim_requested,
size_t max_dim_requested) {
if (min_dim_requested <= dims.size() && dims.size() <= max_dim_requested) {
return true;
}


// Scalar values still support broadcasting
// into arrays with one element.
size_t n_elements = compute_total_size(dims);
return n_elements == 1 && n_dim_requested == 0;
return n_elements == 1 && min_dim_requested == 0;
}

} // namespace details
Expand All @@ -49,8 +52,6 @@ inspector<T> {
// hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector<std::string> => const char*)
using hdf5_type
// Number of dimensions starting from here
static constexpr size_t recursive_ndim
// Is the inner type trivially copyable for optimisation
// If this value is true: data() is mandatory
// If this value is false: serialize, unserialize are mandatory
Expand Down Expand Up @@ -88,10 +89,16 @@ struct type_helper {
using hdf5_type = base_type;

static constexpr size_t ndim = 0;
static constexpr size_t recursive_ndim = ndim;
static constexpr size_t min_ndim = ndim;
static constexpr size_t max_ndim = ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<type>::value;
static constexpr bool is_trivially_nestable = is_trivially_copyable;

static size_t getRank(const type& /* val */) {
return ndim;
}

static std::vector<size_t> getDimensions(const type& /* val */) {
return {};
}
Expand Down Expand Up @@ -216,17 +223,27 @@ struct inspector<std::vector<T>> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = 1;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_nestable;
static constexpr bool is_trivially_nestable = false;

static size_t getRank(const type& val) {
if (!val.empty()) {
return ndim + inspector<value_type>::getRank(val[0]);
} else {
return min_ndim;
}
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes(recursive_ndim, 1ul);
auto rank = getRank(val);
std::vector<size_t> sizes(rank, 1ul);
sizes[0] = val.size();
if (!val.empty()) {
auto s = inspector<value_type>::getDimensions(val[0]);
assert(s.size() + ndim == sizes.size());
for (size_t i = 0; i < s.size(); ++i) {
sizes[i + ndim] = s[i];
}
Expand Down Expand Up @@ -280,10 +297,16 @@ struct inspector<std::vector<bool>> {
using hdf5_type = uint8_t;

static constexpr size_t ndim = 1;
static constexpr size_t recursive_ndim = ndim;
static constexpr size_t min_ndim = ndim;
static constexpr size_t max_ndim = ndim;

static constexpr bool is_trivially_copyable = false;
static constexpr bool is_trivially_nestable = false;

static size_t getRank(const type& /* val */) {
return ndim;
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{val.size()};
return sizes;
Expand Down Expand Up @@ -327,18 +350,22 @@ struct inspector<std::array<T, N>> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = 1;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_nestable;
static constexpr bool is_trivially_nestable = (sizeof(type) == N * sizeof(T)) &&
is_trivially_copyable;

static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val[0]);
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{N};
if (!val.empty()) {
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
}
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}

Expand Down Expand Up @@ -399,11 +426,21 @@ struct inspector<T*> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = 1;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_nestable;
static constexpr bool is_trivially_nestable = false;

static size_t getRank(const type& val) {
if (val != nullptr) {
return ndim + inspector<value_type>::getRank(val[0]);
} else {
return min_ndim;
}
}

static std::vector<size_t> getDimensions(const type& /* val */) {
throw DataSpaceException("Not possible to have size of a T*");
}
Expand All @@ -430,7 +467,9 @@ struct inspector<T[N]> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = 1;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_nestable;
static constexpr bool is_trivially_nestable = is_trivially_copyable;
Expand All @@ -450,12 +489,14 @@ struct inspector<T[N]> {
}
}

static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val[0]);
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{N};
if (N > 0) {
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
}
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}

Expand All @@ -478,5 +519,6 @@ struct inspector<T[N]> {
}
};


} // namespace details
} // namespace HighFive
27 changes: 23 additions & 4 deletions include/highfive/bits/H5ReadWrite_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include <H5Tpublic.h>
#include "H5Inspector_misc.hpp"
#include "H5Utils.hpp"

namespace HighFive {
Expand Down Expand Up @@ -57,10 +58,14 @@ struct BufferInfo {
template <class F>
BufferInfo(const DataType& dtype, F getName, Operation _op);

size_t getRank(const T& array) const;
size_t getMinRank() const;
size_t getMaxRank() const;

// member data for info depending on the destination dataset type
const bool is_fixed_len_string;
const size_t n_dimensions;
const DataType data_type;
const size_t rank_correction;
};

// details implementation
Expand Down Expand Up @@ -135,10 +140,9 @@ BufferInfo<T>::BufferInfo(const DataType& file_data_type, F getName, Operation _
: op(_op)
, is_fixed_len_string(file_data_type.isFixedLenStr())
// In case we are using Fixed-len strings we need to subtract one dimension
, n_dimensions(details::inspector<type_no_const>::recursive_ndim -
((is_fixed_len_string && is_char_array) ? 1 : 0))
, data_type(string_type_checker<char_array_t>::getDataType(create_datatype<elem_type>(),
file_data_type)) {
file_data_type))
, rank_correction((is_fixed_len_string && is_char_array) ? 1 : 0) {
// We warn. In case they are really not convertible an exception will rise on read/write
if (file_data_type.getClass() != data_type.getClass()) {
HIGHFIVE_LOG_WARN(getName() + "\": data and hdf5 dataset have different types: " +
Expand All @@ -157,6 +161,21 @@ BufferInfo<T>::BufferInfo(const DataType& file_data_type, F getName, Operation _
}
}

template <typename T>
size_t BufferInfo<T>::getRank(const T& array) const {
return details::inspector<type_no_const>::getRank(array) - rank_correction;
}

template <typename T>
size_t BufferInfo<T>::getMinRank() const {
return details::inspector<T>::min_ndim - rank_correction;
}

template <typename T>
size_t BufferInfo<T>::getMaxRank() const {
return details::inspector<T>::max_ndim - rank_correction;
}

} // namespace details

} // namespace HighFive
13 changes: 7 additions & 6 deletions include/highfive/bits/H5Slice_traits_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,11 @@ inline void SliceTraits<Derivate>::read(T& array, const DataTransferProps& xfer_
[&slice]() -> std::string { return details::get_dataset(slice).getPath(); },
details::BufferInfo<T>::Operation::read);

if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to read DataSet of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions " << buffer_info.n_dimensions;
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto dims = mem_space.getDimensions();
Expand Down Expand Up @@ -254,11 +255,11 @@ inline void SliceTraits<Derivate>::write(const T& buffer, const DataTransferProp
[&slice]() -> std::string { return details::get_dataset(slice).getPath(); },
details::BufferInfo<T>::Operation::write);

if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to write buffer of dimensions "
<< details::format_vector(mem_space.getDimensions())
<< " into dataset with n = " << buffer_info.n_dimensions << " dimensions.";
ss << "Impossible to write buffer with dimensions n = " << buffer_info.getRank(buffer)
<< "into dataset with dimensions " << details::format_vector(mem_space.getDimensions())
<< ".";
throw DataSpaceException(ss.str());
}
auto w = details::data_converter::serialize<T>(buffer, dims, file_datatype);
Expand Down
30 changes: 24 additions & 6 deletions include/highfive/boost.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,31 @@ struct inspector<boost::multi_array<T, Dims>> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = Dims;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_nestable;
static constexpr bool is_trivially_nestable = false;


static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val.data()[0]);
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes;
auto rank = getRank(val);
std::vector<size_t> sizes(rank, 1ul);
for (size_t i = 0; i < ndim; ++i) {
sizes.push_back(val.shape()[i]);
sizes[i] = val.shape()[i];
}
if (val.size() != 0) {
auto s = inspector<value_type>::getDimensions(val.data()[0]);
sizes.resize(ndim + s.size());
for (size_t i = 0; i < s.size(); ++i) {
sizes[ndim + i] = s[i];
}
}
auto s = inspector<value_type>::getDimensions(val.data()[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}

Expand Down Expand Up @@ -101,11 +113,17 @@ struct inspector<boost::numeric::ublas::matrix<T>> {
using hdf5_type = typename inspector<value_type>::hdf5_type;

static constexpr size_t ndim = 2;
static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;

static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
inspector<value_type>::is_trivially_copyable;
static constexpr bool is_trivially_nestable = false;

static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val(0, 0));
}

static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{val.size1(), val.size2()};
auto s = inspector<value_type>::getDimensions(val(0, 0));
Expand Down

0 comments on commit b3f34bd

Please sign in to comment.