Skip to content

Commit c85b20b

Browse files
authored
Add back port of std::span (open-telemetry#21)
* Start filling out span backport * Fix assertion * Add a span test. * Fix constructors * Fill out span tests * Add test coverage for pointer-count construction * s/ASSERT_DEATH/EXPECT_DEATH/ * Add bracket operator test * Add test coverage for other span construction. * Add test coverage for array construction * Add test for range construction * Fix typo. * Add assignment test * Add iteration test * Add data/size functions * Add array overloads * Support implicit construction from general containers * Reformat * Fix typo * Fix typo * Fix enable_if condition
1 parent c5d421a commit c85b20b

File tree

6 files changed

+544
-0
lines changed

6 files changed

+544
-0
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#pragma once
2+
3+
#include <array>
4+
#include <cassert>
5+
#include <cstddef>
6+
#include <exception>
7+
#include <iterator>
8+
#include <type_traits>
9+
10+
#include "opentelemetry/nostd/utility.h"
11+
12+
namespace opentelemetry
13+
{
14+
namespace nostd
15+
{
16+
constexpr size_t dynamic_extent = static_cast<size_t>(-1);
17+
18+
template <class T, size_t Extent = dynamic_extent>
19+
class span;
20+
21+
namespace detail
22+
{
23+
/**
24+
* Helper class to resolve overloaded constructors
25+
*/
26+
template <class T>
27+
struct is_specialized_span_convertible : std::false_type
28+
{};
29+
30+
template <class T, size_t N>
31+
struct is_specialized_span_convertible<std::array<T, N>> : std::true_type
32+
{};
33+
34+
template <class T, size_t N>
35+
struct is_specialized_span_convertible<T[N]> : std::true_type
36+
{};
37+
38+
template <class T, size_t Extent>
39+
struct is_specialized_span_convertible<span<T, Extent>> : std::true_type
40+
{};
41+
} // namespace detail
42+
43+
/**
44+
* Back port of std::span.
45+
*
46+
* See https://en.cppreference.com/w/cpp/container/span for interface documentation.
47+
*
48+
* Note: This provides a subset of the methods available on std::span.
49+
*
50+
* Note: The std::span API specifies error cases to have undefined behavior, so this implementation
51+
* chooses to terminate or assert rather than throw exceptions.
52+
*/
53+
template <class T, size_t Extent>
54+
class span
55+
{
56+
public:
57+
static constexpr size_t extent = Extent;
58+
59+
// This arcane code is how we make default-construction result in an SFINAE error
60+
// with C++11 when Extent != 0 as specified by the std::span API.
61+
//
62+
// See https://stackoverflow.com/a/10309720/4447365
63+
template <bool B = Extent == 0, typename std::enable_if<B>::type * = nullptr>
64+
span() noexcept : data_{nullptr}
65+
{}
66+
67+
span(T *data, size_t count) noexcept : data_{data}
68+
{
69+
if (count != Extent)
70+
{
71+
std::terminate();
72+
}
73+
}
74+
75+
span(T *first, T *last) noexcept : data_{first}
76+
{
77+
if (std::distance(first, last) != Extent)
78+
{
79+
std::terminate();
80+
}
81+
}
82+
83+
template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
84+
span(T (&array)[N]) noexcept : data_{array}
85+
{}
86+
87+
template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
88+
span(std::array<T, N> &array) noexcept : data_{array.data()}
89+
{}
90+
91+
template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
92+
span(const std::array<T, N> &array) noexcept : data_{array.data()}
93+
{}
94+
95+
template <
96+
class C,
97+
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
98+
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
99+
std::declval<C &>()))>::type (*)[],
100+
T (*)[]>::value &&
101+
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
102+
size_t>::value>::type * = nullptr>
103+
span(C &c) noexcept(noexcept(nostd::data(c), nostd::size(c))) : data_{nostd::data(c)}
104+
{
105+
if (nostd::size(c) != Extent)
106+
{
107+
std::terminate();
108+
}
109+
}
110+
111+
template <
112+
class C,
113+
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
114+
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
115+
std::declval<const C &>()))>::type (*)[],
116+
T (*)[]>::value &&
117+
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
118+
size_t>::value>::type * = nullptr>
119+
span(const C &c) noexcept(noexcept(nostd::data(c), nostd::size(c))) : data_{nostd::data(c)}
120+
{
121+
if (nostd::size(c) != Extent)
122+
{
123+
std::terminate();
124+
}
125+
}
126+
127+
template <class U,
128+
size_t N,
129+
typename std::enable_if<N == Extent &&
130+
std::is_convertible<U (*)[], T (*)[]>::value>::type * = nullptr>
131+
span(const span<U, N> &other) noexcept : data_{other.data()}
132+
{}
133+
134+
span(const span &) noexcept = default;
135+
136+
bool empty() const noexcept { return Extent == 0; }
137+
138+
T *data() const noexcept { return data_; }
139+
140+
size_t size() const noexcept { return Extent; }
141+
142+
T &operator[](size_t index) const noexcept
143+
{
144+
assert(index < Extent);
145+
return data_[index];
146+
}
147+
148+
T *begin() const noexcept { return data_; }
149+
150+
T *end() const noexcept { return data_ + Extent; }
151+
152+
private:
153+
T *data_;
154+
};
155+
156+
template <class T>
157+
class span<T, dynamic_extent>
158+
{
159+
public:
160+
static constexpr size_t extent = dynamic_extent;
161+
162+
span() noexcept : extent_{0}, data_{nullptr} {}
163+
164+
span(T *data, size_t count) noexcept : extent_{count}, data_{data} {}
165+
166+
span(T *first, T *last) noexcept
167+
: extent_{static_cast<size_t>(std::distance(first, last))}, data_{first}
168+
{
169+
assert(first <= last);
170+
}
171+
172+
template <size_t N>
173+
span(T (&array)[N]) noexcept : extent_{N}, data_{array}
174+
{}
175+
176+
template <size_t N>
177+
span(std::array<T, N> &array) noexcept : extent_{N}, data_{array.data()}
178+
{}
179+
180+
template <size_t N>
181+
span(const std::array<T, N> &array) noexcept : extent_{N}, data_{array.data()}
182+
{}
183+
184+
template <
185+
class C,
186+
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
187+
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
188+
std::declval<C &>()))>::type (*)[],
189+
T (*)[]>::value &&
190+
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
191+
size_t>::value>::type * = nullptr>
192+
span(C &c) noexcept(noexcept(nostd::data(c), nostd::size(c)))
193+
: extent_{nostd::size(c)}, data_{nostd::data(c)}
194+
{}
195+
196+
template <
197+
class C,
198+
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
199+
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
200+
std::declval<const C &>()))>::type (*)[],
201+
T (*)[]>::value &&
202+
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
203+
size_t>::value>::type * = nullptr>
204+
span(const C &c) noexcept(noexcept(nostd::data(c), nostd::size(c)))
205+
: extent_{nostd::size(c)}, data_{nostd::data(c)}
206+
{}
207+
208+
template <class U,
209+
size_t N,
210+
typename std::enable_if<std::is_convertible<U (*)[], T (*)[]>::value>::type * = nullptr>
211+
span(const span<U, N> &other) noexcept : extent_{other.size()}, data_{other.data()}
212+
{}
213+
214+
span(const span &) noexcept = default;
215+
216+
bool empty() const noexcept { return extent_ == 0; }
217+
218+
T *data() const noexcept { return data_; }
219+
220+
size_t size() const noexcept { return extent_; }
221+
222+
T &operator[](size_t index) const noexcept
223+
{
224+
assert(index < extent_);
225+
return data_[index];
226+
}
227+
228+
T *begin() const noexcept { return data_; }
229+
230+
T *end() const noexcept { return data_ + extent_; }
231+
232+
private:
233+
// Note: matches libstdc++'s layout for std::span
234+
// See
235+
// https://github.com/gcc-mirror/gcc/blob/a60701e05b3878000ff9fdde1aecbc472b9dec5a/libstdc%2B%2B-v3/include/std/span#L402-L403
236+
size_t extent_;
237+
T *data_;
238+
};
239+
} // namespace nostd
240+
} // namespace opentelemetry
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <initializer_list>
5+
6+
namespace opentelemetry
7+
{
8+
namespace nostd
9+
{
10+
/**
11+
* Back port of std::data
12+
*
13+
* See https://en.cppreference.com/w/cpp/iterator/data
14+
*/
15+
template <class C>
16+
auto data(C &c) noexcept(noexcept(c.data())) -> decltype(c.data())
17+
{
18+
return c.data();
19+
}
20+
21+
template <class C>
22+
auto data(const C &c) noexcept(noexcept(c.data())) -> decltype(c.data())
23+
{
24+
return c.data();
25+
}
26+
27+
template <class T, size_t N>
28+
T *data(T (&array)[N]) noexcept
29+
{
30+
return array;
31+
}
32+
33+
template <class E>
34+
const E *data(std::initializer_list<E> list) noexcept
35+
{
36+
return list.begin();
37+
}
38+
39+
/**
40+
* Back port of std::size
41+
*
42+
* See https://en.cppreference.com/w/cpp/iterator/size
43+
*/
44+
template <class C>
45+
auto size(const C &c) noexcept(noexcept(c.size())) -> decltype(c.size())
46+
{
47+
return c.size();
48+
}
49+
50+
template <class T, size_t N>
51+
size_t size(T (&array)[N]) noexcept
52+
{
53+
return N;
54+
}
55+
} // namespace nostd
56+
} // namespace opentelemetry

api/test/nostd/BUILD

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,25 @@ cc_test(
88
"@com_google_googletest//:gtest_main",
99
],
1010
)
11+
12+
cc_test(
13+
name = "utility_test",
14+
srcs = [
15+
"utility_test.cc",
16+
],
17+
deps = [
18+
"//api",
19+
"@com_google_googletest//:gtest_main",
20+
],
21+
)
22+
23+
cc_test(
24+
name = "span_test",
25+
srcs = [
26+
"span_test.cc",
27+
],
28+
deps = [
29+
"//api",
30+
"@com_google_googletest//:gtest_main",
31+
],
32+
)

api/test/nostd/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,13 @@ target_link_libraries(string_view_test ${GTEST_BOTH_LIBRARIES}
55
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
66
gtest_add_tests(TARGET string_view_test TEST_PREFIX nostd. TEST_LIST
77
string_view_test)
8+
9+
add_executable(utility_test utility_test.cc)
10+
target_link_libraries(utility_test ${GTEST_BOTH_LIBRARIES}
11+
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
12+
gtest_add_tests(TARGET utility_test TEST_PREFIX nostd. TEST_LIST utility_test)
13+
14+
add_executable(span_test span_test.cc)
15+
target_link_libraries(span_test ${GTEST_BOTH_LIBRARIES}
16+
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
17+
gtest_add_tests(TARGET span_test TEST_PREFIX nostd. TEST_LIST span_test)

0 commit comments

Comments
 (0)