Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit dea3eab

Browse files
authored
Support boost::span. (#1025)
1 parent d7da804 commit dea3eab

File tree

9 files changed

+211
-97
lines changed

9 files changed

+211
-97
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,13 @@ jobs:
189189
# Job running unit-test with sanitizers
190190
# =====================================
191191
Linux_Sanitizers:
192-
runs-on: ubuntu-20.04
192+
runs-on: ubuntu-24.04
193193
strategy:
194194
matrix:
195195
env: [
196-
{CC: clang-12, CXX: clang++-12, HIGHFIVE_SANITIZER: address},
197-
{CC: clang-12, CXX: clang++-12, HIGHFIVE_SANITIZER: undefined},
198-
{CC: gcc-10, CXX: g++-10, HIGHFIVE_GLIBCXX_ASSERTIONS: On},
196+
{CC: clang, CXX: clang++, HIGHFIVE_SANITIZER: address},
197+
{CC: clang, CXX: clang++, HIGHFIVE_SANITIZER: undefined},
198+
{CC: gcc, CXX: g++, HIGHFIVE_GLIBCXX_ASSERTIONS: On},
199199
]
200200

201201
steps:
@@ -206,14 +206,15 @@ jobs:
206206
- name: "Install libraries"
207207
run: |
208208
sudo apt-get -qq update
209-
sudo apt-get -qq install libboost-all-dev libeigen3-dev libhdf5-dev libsz2 ninja-build
209+
sudo apt-get -qq install boost1.83 libeigen3-dev libhdf5-dev libsz2 ninja-build
210210
211211
- name: Build
212212
env: ${{matrix.env}}
213213
run: |
214214
CMAKE_OPTIONS=(
215215
-GNinja
216216
-DHIGHFIVE_TEST_BOOST:BOOL=ON
217+
-DHIGHFIVE_TEST_BOOST_SPAN:BOOL=ON
217218
-DHIGHFIVE_TEST_EIGEN:BOOL=ON
218219
-DHIGHFIVE_BUILD_DOCS:BOOL=FALSE
219220
-DHIGHFIVE_GLIBCXX_ASSERTIONS=${HIGHFIVE_GLIBCXX_ASSERTIONS:-OFF}

CMakeLists.txt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ option(HIGHFIVE_UNIT_TESTS "Compile unit-tests" ${HIGHFIVE_EXTRAS_DEFAULT})
5959
option(HIGHFIVE_EXAMPLES "Compile examples" ${HIGHFIVE_EXTRAS_DEFAULT})
6060
option(HIGHFIVE_BUILD_DOCS "Build documentation" ${HIGHFIVE_EXTRAS_DEFAULT})
6161

62-
option(HIGHFIVE_TEST_SPAN "Enable std::span testing, requires C++20" ${HIGHFIVE_TEST_SPAN_DEFAULT})
63-
option(HIGHFIVE_TEST_BOOST "Enable Boost testing" OFF)
64-
option(HIGHFIVE_TEST_EIGEN "Enable Eigen testing" OFF)
65-
option(HIGHFIVE_TEST_OPENCV "Enable OpenCV testing" OFF)
66-
option(HIGHFIVE_TEST_XTENSOR "Enable xtensor testing" OFF)
67-
option(HIGHFIVE_TEST_HALF_FLOAT "Enable half-precision floats" OFF)
62+
option(HIGHFIVE_TEST_SPAN "Enable testing std::span, requires C++20" ${HIGHFIVE_TEST_SPAN_DEFAULT})
63+
option(HIGHFIVE_TEST_BOOST "Enable testing Boost features" OFF)
64+
option(HIGHFIVE_TEST_BOOST_SPAN "Additionally, enable testing `boost::span`" OFF)
65+
option(HIGHFIVE_TEST_EIGEN "Enable testing Eigen" OFF)
66+
option(HIGHFIVE_TEST_OPENCV "Enable testing OpenCV" OFF)
67+
option(HIGHFIVE_TEST_XTENSOR "Enable testing xtensor" OFF)
68+
option(HIGHFIVE_TEST_HALF_FLOAT "Enable testing half-precision floats" OFF)
6869

6970
# TODO remove entirely.
7071
option(HIGHFIVE_HAS_CONCEPTS "Print readable compiler errors w/ C++20 concepts" OFF)

cmake/HighFiveOptionalDependencies.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ if(NOT TARGET HighFiveBoostDependency)
77
# -DBOOST_ALL_NO_LIB (does something on MSVC).
88
target_compile_definitions(HighFiveBoostDependency INTERFACE HIGHFIVE_TEST_BOOST=1)
99
endif()
10+
if(HIGHFIVE_TEST_BOOST_SPAN)
11+
target_compile_definitions(HighFiveBoostDependency INTERFACE HIGHFIVE_TEST_BOOST_SPAN=1)
12+
endif()
1013
endif()
1114

1215
if(NOT TARGET HighFiveEigenDependency)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#pragma once
2+
3+
#include "H5Inspector_decl.hpp"
4+
#include "../H5Exception.hpp"
5+
6+
#include <cstdlib>
7+
#include <vector>
8+
#include <type_traits>
9+
10+
namespace HighFive {
11+
namespace details {
12+
13+
14+
// Anything with the same API as `std::span` can implemented by inheriting from
15+
// this class.
16+
template <class Span>
17+
struct inspector_stl_span {
18+
using type = Span;
19+
using value_type = unqualified_t<typename Span::value_type>;
20+
using base_type = typename inspector<value_type>::base_type;
21+
using hdf5_type = typename inspector<value_type>::hdf5_type;
22+
23+
static constexpr size_t ndim = 1;
24+
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
25+
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;
26+
27+
static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
28+
inspector<value_type>::is_trivially_nestable;
29+
static constexpr bool is_trivially_nestable = false;
30+
31+
32+
static size_t getRank(const type& val) {
33+
if (!val.empty()) {
34+
return ndim + inspector<value_type>::getRank(val[0]);
35+
} else {
36+
return min_ndim;
37+
}
38+
}
39+
40+
static std::vector<size_t> getDimensions(const type& val) {
41+
auto rank = getRank(val);
42+
std::vector<size_t> sizes(rank, 1ul);
43+
sizes[0] = val.size();
44+
if (!val.empty()) {
45+
auto s = inspector<value_type>::getDimensions(val[0]);
46+
assert(s.size() + ndim == sizes.size());
47+
for (size_t i = 0; i < s.size(); ++i) {
48+
sizes[i + ndim] = s[i];
49+
}
50+
}
51+
return sizes;
52+
}
53+
54+
static void prepare(type& val, const std::vector<size_t>& expected_dims) {
55+
auto actual_dims = getDimensions(val);
56+
if (actual_dims.size() != expected_dims.size()) {
57+
throw DataSpaceException("Mismatching rank.");
58+
}
59+
60+
for (size_t i = 0; i < actual_dims.size(); ++i) {
61+
if (actual_dims[i] != expected_dims[i]) {
62+
throw DataSpaceException("Mismatching dimensions.");
63+
}
64+
}
65+
}
66+
67+
static hdf5_type* data(type& val) {
68+
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
69+
}
70+
71+
static const hdf5_type* data(const type& val) {
72+
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
73+
}
74+
75+
template <class It>
76+
static void serialize(const type& val, const std::vector<size_t>& dims, It m) {
77+
if (!val.empty()) {
78+
auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end());
79+
size_t subsize = compute_total_size(subdims);
80+
for (const auto& e: val) {
81+
inspector<value_type>::serialize(e, subdims, m);
82+
m += subsize;
83+
}
84+
}
85+
}
86+
87+
template <class It>
88+
static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
89+
std::vector<size_t> subdims(dims.begin() + ndim, dims.end());
90+
size_t subsize = compute_total_size(subdims);
91+
for (size_t i = 0; i < dims[0]; ++i) {
92+
inspector<value_type>::unserialize(vec_align + i * subsize, subdims, val[i]);
93+
}
94+
}
95+
};
96+
97+
} // namespace details
98+
} // namespace HighFive

include/highfive/boost_span.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include "bits/H5Inspector_decl.hpp"
4+
#include "H5Exception.hpp"
5+
#include "bits/inspector_stl_span_misc.hpp"
6+
7+
#include <boost/core/span.hpp>
8+
9+
namespace HighFive {
10+
namespace details {
11+
template <class T, std::size_t Extent>
12+
struct inspector<boost::span<T, Extent>>: public inspector_stl_span<boost::span<T, Extent>> {
13+
private:
14+
using super = inspector_stl_span<boost::span<T, Extent>>;
15+
16+
public:
17+
using type = typename super::type;
18+
using value_type = typename super::value_type;
19+
using base_type = typename super::base_type;
20+
using hdf5_type = typename super::hdf5_type;
21+
};
22+
23+
} // namespace details
24+
} // namespace HighFive

include/highfive/span.hpp

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,92 +10,23 @@
1010
#pragma once
1111

1212
#include "bits/H5Inspector_decl.hpp"
13-
#include "H5Exception.hpp"
13+
#include "bits/inspector_stl_span_misc.hpp"
1414

1515
#include <span>
1616

1717
namespace HighFive {
1818
namespace details {
1919

2020
template <class T, std::size_t Extent>
21-
struct inspector<std::span<T, Extent>> {
22-
using type = std::span<T, Extent>;
23-
using value_type = unqualified_t<T>;
24-
using base_type = typename inspector<value_type>::base_type;
25-
using hdf5_type = typename inspector<value_type>::hdf5_type;
26-
27-
static constexpr size_t ndim = 1;
28-
static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim;
29-
static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim;
30-
31-
static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
32-
inspector<value_type>::is_trivially_nestable;
33-
static constexpr bool is_trivially_nestable = false;
34-
35-
36-
static size_t getRank(const type& val) {
37-
if (!val.empty()) {
38-
return ndim + inspector<value_type>::getRank(val[0]);
39-
} else {
40-
return min_ndim;
41-
}
42-
}
43-
44-
static std::vector<size_t> getDimensions(const type& val) {
45-
auto rank = getRank(val);
46-
std::vector<size_t> sizes(rank, 1ul);
47-
sizes[0] = val.size();
48-
if (!val.empty()) {
49-
auto s = inspector<value_type>::getDimensions(val[0]);
50-
assert(s.size() + ndim == sizes.size());
51-
for (size_t i = 0; i < s.size(); ++i) {
52-
sizes[i + ndim] = s[i];
53-
}
54-
}
55-
return sizes;
56-
}
57-
58-
static void prepare(type& val, const std::vector<size_t>& expected_dims) {
59-
auto actual_dims = getDimensions(val);
60-
if (actual_dims.size() != expected_dims.size()) {
61-
throw DataSpaceException("Mismatching rank.");
62-
}
63-
64-
for (size_t i = 0; i < actual_dims.size(); ++i) {
65-
if (actual_dims[i] != expected_dims[i]) {
66-
throw DataSpaceException("Mismatching dimensions.");
67-
}
68-
}
69-
}
70-
71-
static hdf5_type* data(type& val) {
72-
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
73-
}
74-
75-
static const hdf5_type* data(const type& val) {
76-
return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
77-
}
78-
79-
template <class It>
80-
static void serialize(const type& val, const std::vector<size_t>& dims, It m) {
81-
if (!val.empty()) {
82-
auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end());
83-
size_t subsize = compute_total_size(subdims);
84-
for (const auto& e: val) {
85-
inspector<value_type>::serialize(e, subdims, m);
86-
m += subsize;
87-
}
88-
}
89-
}
90-
91-
template <class It>
92-
static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
93-
std::vector<size_t> subdims(dims.begin() + ndim, dims.end());
94-
size_t subsize = compute_total_size(subdims);
95-
for (size_t i = 0; i < dims[0]; ++i) {
96-
inspector<value_type>::unserialize(vec_align + i * subsize, subdims, val[i]);
97-
}
98-
}
21+
struct inspector<std::span<T, Extent>>: public inspector_stl_span<std::span<T, Extent>> {
22+
private:
23+
using super = inspector_stl_span<std::span<T, Extent>>;
24+
25+
public:
26+
using type = typename super::type;
27+
using value_type = typename super::value_type;
28+
using base_type = typename super::base_type;
29+
using hdf5_type = typename super::hdf5_type;
9930
};
10031

10132
} // namespace details

tests/unit/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ foreach(PUBLIC_HEADER ${public_headers})
5555
continue()
5656
endif()
5757

58+
if(PUBLIC_HEADER STREQUAL "highfive/boost_span.hpp" AND NOT HIGHFIVE_TEST_BOOST_SPAN)
59+
continue()
60+
endif()
61+
5862
if(PUBLIC_HEADER STREQUAL "highfive/half_float.hpp" AND NOT HIGHFIVE_TEST_HALF_FLOAT)
5963
continue()
6064
endif()

tests/unit/data_generator.hpp

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include <highfive/boost.hpp>
1414
#endif
1515

16+
#ifdef HIGHFIVE_TEST_BOOST_SPAN
17+
#include <highfive/boost_span.hpp>
18+
#endif
19+
1620
#ifdef HIGHFIVE_TEST_EIGEN
1721
#include <highfive/eigen.hpp>
1822
#endif
@@ -236,12 +240,15 @@ struct ContainerTraits<std::array<T, N>>: public STLLikeContainerTraits<std::arr
236240
}
237241
};
238242

239-
240-
#ifdef HIGHFIVE_TEST_SPAN
241-
template <class T, std::size_t Extent>
242-
struct ContainerTraits<std::span<T, Extent>>: public STLLikeContainerTraits<std::span<T, Extent>> {
243+
// Anything with the same API as `std::span` can implemented by inheriting from
244+
// this class.
245+
//
246+
// The template parameter `DynamicExtent` is the equivalent of the magic number
247+
// `std::dynamic_extent`.
248+
template <class Span, size_t DynamicExtent>
249+
struct STLSpanLikeContainerTraits: public STLLikeContainerTraits<Span> {
243250
private:
244-
using super = STLLikeContainerTraits<std::span<T, Extent>>;
251+
using super = STLLikeContainerTraits<Span>;
245252

246253
public:
247254
using container_type = typename super::container_type;
@@ -274,12 +281,26 @@ struct ContainerTraits<std::span<T, Extent>>: public STLLikeContainerTraits<std:
274281
}
275282

276283
static void sanitize_dims(std::vector<size_t>& dims, size_t axis) {
277-
if (Extent != std::dynamic_extent) {
278-
dims[axis] = Extent;
284+
if (Span::extent != DynamicExtent) {
285+
dims[axis] = Span::extent;
279286
ContainerTraits<value_type>::sanitize_dims(dims, axis + 1);
280287
}
281288
}
282289
};
290+
291+
292+
#ifdef HIGHFIVE_TEST_SPAN
293+
template <class T, std::size_t Extent>
294+
struct ContainerTraits<std::span<T, Extent>>
295+
: public STLSpanLikeContainerTraits<std::span<T, Extent>, std::dynamic_extent> {
296+
private:
297+
using super = STLSpanLikeContainerTraits<std::span<T, Extent>, std::dynamic_extent>;
298+
299+
public:
300+
using container_type = typename super::container_type;
301+
using value_type = typename super::value_type;
302+
using base_type = typename super::base_type;
303+
};
283304
#endif
284305

285306

@@ -428,6 +449,20 @@ struct ContainerTraits<boost::numeric::ublas::matrix<T>> {
428449

429450
#endif
430451

452+
#if HIGHFIVE_TEST_BOOST_SPAN
453+
template <class T, std::size_t Extent>
454+
struct ContainerTraits<boost::span<T, Extent>>
455+
: public STLSpanLikeContainerTraits<boost::span<T, Extent>, boost::dynamic_extent> {
456+
private:
457+
using super = STLSpanLikeContainerTraits<boost::span<T, Extent>, boost::dynamic_extent>;
458+
459+
public:
460+
using container_type = typename super::container_type;
461+
using value_type = typename super::value_type;
462+
using base_type = typename super::base_type;
463+
};
464+
#endif
465+
431466
// -- Eigen -------------------------------------------------------------------
432467
#if HIGHFIVE_TEST_EIGEN
433468

0 commit comments

Comments
 (0)