Program Listing for File h5dispatch.hpp

↰ Return to documentation for file (include/dx2/h5/h5dispatch.hpp)

#pragma once

#include "dx2/h5/h5utils.hpp"
#include "dx2/logging.hpp"
#include <hdf5.h>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <variant>

using namespace h5utils;

namespace h5dispatch {

template <typename T> struct type_tag_t {
  using type = T;
};
template <typename T> constexpr type_tag_t<T> type_tag{};

enum class BoolEnum : uint8_t { FALSE = 0, TRUE = 1 };

template <typename... Ts> struct type_list {};

using ColumnTypes = type_list<int, int64_t, uint64_t, double, BoolEnum>;

template <typename T, typename List> struct type_list_contains;
template <typename T, typename... Ts>
struct type_list_contains<T, type_list<Ts...>>
    : std::bool_constant<(std::is_same_v<T, Ts> || ...)> {};

template <typename T>
inline constexpr bool is_column_type_v =
    type_list_contains<T, ColumnTypes>::value;

template <template <typename> class Wrap, typename List> struct variant_of;
template <template <typename> class Wrap, typename... Ts>
struct variant_of<Wrap, type_list<Ts...>> {
  using type = std::variant<Wrap<Ts>...>;
};

template <template <typename> class Wrap, typename List>
using variant_of_t = typename variant_of<Wrap, List>::type;

template <typename T> struct H5TypeTraits; // Primary template, never defined

#pragma region H5TypeTraits specialisations
template <> struct H5TypeTraits<int> {
  static constexpr H5T_class_t h5_class = H5T_INTEGER;
  static constexpr H5T_sign_t h5_sign = H5T_SGN_2;
  static H5Type make() { return H5Type(H5Tcopy(H5T_NATIVE_INT)); }
};

template <> struct H5TypeTraits<int64_t> {
  static constexpr H5T_class_t h5_class = H5T_INTEGER;
  static constexpr H5T_sign_t h5_sign = H5T_SGN_2;
  static H5Type make() { return H5Type(H5Tcopy(H5T_NATIVE_LLONG)); }
};

template <> struct H5TypeTraits<uint64_t> {
  static constexpr H5T_class_t h5_class = H5T_INTEGER;
  static constexpr H5T_sign_t h5_sign = H5T_SGN_NONE;
  static H5Type make() { return H5Type(H5Tcopy(H5T_NATIVE_ULLONG)); }
};

template <> struct H5TypeTraits<double> {
  static constexpr H5T_class_t h5_class = H5T_FLOAT;
  static constexpr H5T_sign_t h5_sign = H5T_SGN_ERROR;
  static H5Type make() { return H5Type(H5Tcopy(H5T_NATIVE_DOUBLE)); }
};

template <> struct H5TypeTraits<BoolEnum> {
  static constexpr H5T_class_t h5_class = H5T_ENUM;
  static constexpr H5T_sign_t h5_sign = H5T_SGN_NONE;
  static H5Type make() {
    H5Type enum_type(H5Tenum_create(H5T_NATIVE_UINT8));
    uint8_t false_val = 0;
    uint8_t true_val = 1;
    H5Tenum_insert(enum_type, "FALSE", &false_val);
    H5Tenum_insert(enum_type, "TRUE", &true_val);
    return enum_type;
  }
};
#pragma endregion

template <typename T> H5Type make_h5_type() {
  static_assert(is_column_type_v<T>, "T is not in h5dispatch::ColumnTypes");
  H5Type type = H5TypeTraits<T>::make();
  if (!type) {
    throw std::runtime_error("Failed to create HDF5 datatype.");
  }
  return type;
}

template <typename F, typename... Ts>
bool any_of_types(type_list<Ts...>, F &&f) {
  return (f(type_tag<Ts>) || ...);
}

template <typename Callback>
void dispatch_h5_dataset_type(hid_t dataset_id, Callback &&cb) {
  H5Type type_id(H5Dget_type(dataset_id));
  if (!type_id) {
    throw std::runtime_error("Failed to get dataset type.");
  }

  H5T_class_t cls = H5Tget_class(type_id);
  size_t size = H5Tget_size(type_id);
  H5T_sign_t sign = H5Tget_sign(type_id);

  bool matched = any_of_types(ColumnTypes{}, [&](auto tag) {
    using T = typename decltype(tag)::type;
    using Traits = H5TypeTraits<T>;
    if (Traits::h5_class != cls || sizeof(T) != size ||
        (cls == H5T_INTEGER && Traits::h5_sign != sign)) {
      return false;
    }
    cb(tag);
    return true;
  });

  if (!matched) {
    dx2_log::error("Unsupported HDF5 dataset type:");
    fmt::print(stderr,
               "  • Class: {}\n"
               "  • Size : {}\n"
               "  • Sign : {}\n",
               static_cast<int>(cls), size, static_cast<int>(sign));
    throw std::runtime_error("No matching column type.");
  }
}

} // namespace h5dispatch