Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support std::span. #987

Merged
merged 3 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -36,6 +36,9 @@
#include <highfive/eigen.hpp>
#endif

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

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