Skip to content

Commit

Permalink
Support std::span. (#987)
Browse files Browse the repository at this point in the history
  • Loading branch information
1uc committed May 7, 2024
1 parent d3f82e2 commit 0a921cb
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,18 @@ else()
set(HIGHFIVE_EXTRAS_DEFAULT OFF)
endif()

if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
include(CheckIncludeFileCXX)
CHECK_INCLUDE_FILE_CXX(span HIGHFIVE_TEST_SPAN_DEFAULT)
else()
set(HIGHFIVE_TEST_SPAN_DEFAULT Off)
endif()

option(HIGHFIVE_UNIT_TESTS "Compile unit-tests" ${HIGHFIVE_EXTRAS_DEFAULT})
option(HIGHFIVE_EXAMPLES "Compile examples" ${HIGHFIVE_EXTRAS_DEFAULT})
option(HIGHFIVE_BUILD_DOCS "Build documentation" ${HIGHFIVE_EXTRAS_DEFAULT})

option(HIGHFIVE_TEST_SPAN "Enable std::span testing, requires C++20" ${HIGHFIVE_TEST_SPAN_DEFAULT})
option(HIGHFIVE_TEST_BOOST "Enable Boost testing" OFF)
option(HIGHFIVE_TEST_EIGEN "Enable Eigen testing" OFF)
option(HIGHFIVE_TEST_OPENCV "Enable OpenCV testing" OFF)
Expand Down
8 changes: 8 additions & 0 deletions cmake/HighFiveOptionalDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,20 @@ if(NOT TARGET HighFiveOpenCVDependency)
endif()
endif()

if(NOT TARGET HighFiveSpanDependency)
add_library(HighFiveSpanDependency INTERFACE)
if(HIGHFIVE_TEST_SPAN)
target_compile_definitions(HighFiveSpanDependency INTERFACE HIGHFIVE_TEST_SPAN=1)
endif()
endif()

if(NOT TARGET HighFiveOptionalDependencies)
add_library(HighFiveOptionalDependencies INTERFACE)
target_link_libraries(HighFiveOptionalDependencies INTERFACE
HighFiveBoostDependency
HighFiveEigenDependency
HighFiveXTensorDependency
HighFiveOpenCVDependency
HighFiveSpanDependency
)
endif()
1 change: 1 addition & 0 deletions include/highfive/bits/H5Inspector_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ struct inspector<std::array<T, N>> {
}
};


// Cannot be use for reading
template <typename T>
struct inspector<T*> {
Expand Down
90 changes: 90 additions & 0 deletions include/highfive/span.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2024 Blue Brain Project
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
*/

#pragma once

#include "bits/H5Inspector_decl.hpp"

#include <span>

namespace HighFive {
namespace details {

template <class T, std::size_t Extent>
struct inspector<std::span<T, Extent>> {
using type = std::span<T, Extent>;
using value_type = unqualified_t<T>;
using base_type = typename inspector<value_type>::base_type;
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 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 std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes(recursive_ndim, 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];
}
}
return sizes;
}

static void prepare(type& val, const std::vector<size_t>& expected_dims) {
auto actual_dims = getDimensions(val);
if (actual_dims.size() != expected_dims.size()) {
throw DataSpaceException("Mismatching rank.");
}

for (size_t i = 0; i < actual_dims.size(); ++i) {
if (actual_dims[i] != expected_dims[i]) {
throw DataSpaceException("Mismatching dimensions.");
}
}
}

static hdf5_type* data(type& val) {
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
}

static const hdf5_type* data(const type& val) {
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
}

template <class It>
static void serialize(const type& val, const std::vector<size_t>& dims, It m) {
if (!val.empty()) {
auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end());
size_t subsize = compute_total_size(subdims);
for (const auto& e: val) {
inspector<value_type>::serialize(e, subdims, m);
m += subsize;
}
}
}

template <class It>
static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
std::vector<size_t> subdims(dims.begin() + ndim, dims.end());
size_t subsize = compute_total_size(subdims);
for (size_t i = 0; i < dims[0]; ++i) {
inspector<value_type>::unserialize(vec_align + i * subsize, subdims, val[i]);
}
}
};

} // namespace details
} // namespace HighFive
10 changes: 10 additions & 0 deletions src/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ set(core_examples
${CMAKE_CURRENT_SOURCE_DIR}/select_partial_dataset_cpp11.cpp
)

set(span_examples
${CMAKE_CURRENT_SOURCE_DIR}/read_write_std_span.cpp
)

set(easy_examples
${CMAKE_CURRENT_SOURCE_DIR}/easy_attribute.cpp
${CMAKE_CURRENT_SOURCE_DIR}/easy_dumpoptions.cpp
Expand Down Expand Up @@ -70,6 +74,12 @@ foreach(example_source ${easy_examples})
compile_example(${example_source})
endforeach()

if(HIGHFIVE_TEST_SPAN)
foreach(example_source ${span_examples})
compile_example(${example_source})
endforeach()
endif()

if(HIGHFIVE_TEST_BOOST)
foreach(example_source ${boost_examples})
compile_example(${example_source} HighFiveBoostDependency)
Expand Down
56 changes: 56 additions & 0 deletions src/examples/read_write_std_span.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c), 2024, Blue Brain Project
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
*/

// This example demonstrates using `std::span`. An `std::span` is a pointer
// with a size.

#include <string>
#include <vector>

#include <highfive/highfive.hpp>

#include <highfive/span.hpp>

int main(void) {
using namespace HighFive;

std::string file_name = "read_write_span.h5";
std::string dataset_name = "array";

File file(file_name, File::Truncate);

// Let's write to file.
{
// Assume we have one-dimensional data in some unsupported format (we
// use `std::vector` for simplicity). Further, assume that the data is
// stored contiguously. Then one can create an `std::span`.
std::vector<double> values{1.0, 2.0, 3.0};
auto view = std::span<double>(values.data(), values.size());

// Given the span, HighFive can deduce the shape of the dataset. Hence,
// spans are fully supported when writing. For example:
auto dataset = file.createDataSet(dataset_name, view);
}

// Let's read from file.
{
auto dataset = file.getDataSet(dataset_name);

// Since spans are views, HighFive can't (or wont) allocate memory.
// Instead one must preallocate memory and then create a span for that
// memory:
auto values = std::vector<double>(dataset.getElementCount());
auto view = std::span<double>(values.data(), values.size());

// ... now we can read into the preallocated memory:
dataset.read(view);
}

return 0;
}
51 changes: 51 additions & 0 deletions tests/unit/data_generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include <highfive/eigen.hpp>
#endif

#ifdef HIGHFIVE_TEST_SPAN
#include <highfive/span.hpp>
#endif


namespace HighFive {
namespace testing {
Expand Down Expand Up @@ -221,6 +225,53 @@ struct ContainerTraits<std::array<T, N>>: public STLLikeContainerTraits<std::arr
}
};


#ifdef HIGHFIVE_TEST_SPAN
template <class T, std::size_t Extent>
struct ContainerTraits<std::span<T, Extent>>: public STLLikeContainerTraits<std::span<T, Extent>> {
private:
using super = STLLikeContainerTraits<std::span<T, Extent>>;

public:
using container_type = typename super::container_type;
using value_type = typename super::value_type;
using base_type = typename super::base_type;

static constexpr bool is_view = true;

static container_type allocate(const std::vector<size_t>& dims) {
size_t n_elements = dims[0];
value_type* ptr = new value_type[n_elements];

container_type array = container_type(ptr, n_elements);

for (size_t i = 0; i < n_elements; ++i) {
auto element = ContainerTraits<value_type>::allocate(lstrip(dims, 1));
ContainerTraits<value_type>::assign(array[i], element);
}

return array;
}

static void deallocate(container_type& array, const std::vector<size_t>& dims) {
size_t n_elements = dims[0];
for (size_t i = 0; i < n_elements; ++i) {
ContainerTraits<value_type>::deallocate(array[i], lstrip(dims, 1));
}

delete[] array.data();
}

static void sanitize_dims(std::vector<size_t>& dims, size_t axis) {
if (Extent != std::dynamic_extent) {
dims[axis] = Extent;
ContainerTraits<value_type>::sanitize_dims(dims, axis + 1);
}
}
};
#endif


// -- Boost -------------------------------------------------------------------
#ifdef HIGHFIVE_TEST_BOOST
template <class T, size_t n>
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/supported_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ struct STDArray {
using type = std::array<typename C::template type<T>, n>;
};

#ifdef HIGHFIVE_TEST_SPAN
template <class C = type_identity>
struct STDSpan {
template <class T>
using type = std::span<typename C::template type<T>>;
};
#endif

#ifdef HIGHFIVE_TEST_BOOST
template <size_t n, class C = type_identity>
struct BoostMultiArray {
Expand Down Expand Up @@ -150,6 +158,13 @@ using supported_array_types = typename ConcatenateTuples<
typename ContainerProduct<STDArray<7, EigenMatrix<3, 5, Eigen::RowMajor>>, scalar_types_eigen>::type,
typename ContainerProduct<STDArray<7, EigenArray<Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>, scalar_types_eigen>::type,
std::tuple<std::array<Eigen::VectorXd, 7>>,
#endif
#ifdef HIGHFIVE_TEST_SPAN
typename ContainerProduct<STDSpan<>, all_scalar_types>::type,
typename ContainerProduct<STDVector<STDSpan<>>, some_scalar_types>::type,
typename ContainerProduct<STDArray<7, STDSpan<>>, some_scalar_types>::type,
typename ContainerProduct<STDSpan<STDVector<>>, some_scalar_types>::type,
typename ContainerProduct<STDSpan<STDArray<3>>, some_scalar_types>::type,
#endif
typename ContainerProduct<STDVector<>, all_scalar_types>::type,
typename ContainerProduct<STDVector<STDVector<>>, some_scalar_types>::type,
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/tests_high_five_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
#include <highfive/eigen.hpp>
#endif

#ifdef HIGHFIVE_TEST_SPAN
#include <highfive/span.hpp>
#endif

using namespace HighFive;
using Catch::Matchers::Equals;
Expand Down

0 comments on commit 0a921cb

Please sign in to comment.