Skip to content

Commit

Permalink
Add back port of std::span (open-telemetry#21)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rnburn authored Jan 7, 2020
1 parent c5d421a commit c85b20b
Show file tree
Hide file tree
Showing 6 changed files with 544 additions and 0 deletions.
240 changes: 240 additions & 0 deletions api/include/opentelemetry/nostd/span.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#pragma once

#include <array>
#include <cassert>
#include <cstddef>
#include <exception>
#include <iterator>
#include <type_traits>

#include "opentelemetry/nostd/utility.h"

namespace opentelemetry
{
namespace nostd
{
constexpr size_t dynamic_extent = static_cast<size_t>(-1);

template <class T, size_t Extent = dynamic_extent>
class span;

namespace detail
{
/**
* Helper class to resolve overloaded constructors
*/
template <class T>
struct is_specialized_span_convertible : std::false_type
{};

template <class T, size_t N>
struct is_specialized_span_convertible<std::array<T, N>> : std::true_type
{};

template <class T, size_t N>
struct is_specialized_span_convertible<T[N]> : std::true_type
{};

template <class T, size_t Extent>
struct is_specialized_span_convertible<span<T, Extent>> : std::true_type
{};
} // namespace detail

/**
* Back port of std::span.
*
* See https://en.cppreference.com/w/cpp/container/span for interface documentation.
*
* Note: This provides a subset of the methods available on std::span.
*
* Note: The std::span API specifies error cases to have undefined behavior, so this implementation
* chooses to terminate or assert rather than throw exceptions.
*/
template <class T, size_t Extent>
class span
{
public:
static constexpr size_t extent = Extent;

// This arcane code is how we make default-construction result in an SFINAE error
// with C++11 when Extent != 0 as specified by the std::span API.
//
// See https://stackoverflow.com/a/10309720/4447365
template <bool B = Extent == 0, typename std::enable_if<B>::type * = nullptr>
span() noexcept : data_{nullptr}
{}

span(T *data, size_t count) noexcept : data_{data}
{
if (count != Extent)
{
std::terminate();
}
}

span(T *first, T *last) noexcept : data_{first}
{
if (std::distance(first, last) != Extent)
{
std::terminate();
}
}

template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
span(T (&array)[N]) noexcept : data_{array}
{}

template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
span(std::array<T, N> &array) noexcept : data_{array.data()}
{}

template <size_t N, typename std::enable_if<Extent == N>::type * = nullptr>
span(const std::array<T, N> &array) noexcept : data_{array.data()}
{}

template <
class C,
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
std::declval<C &>()))>::type (*)[],
T (*)[]>::value &&
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
size_t>::value>::type * = nullptr>
span(C &c) noexcept(noexcept(nostd::data(c), nostd::size(c))) : data_{nostd::data(c)}
{
if (nostd::size(c) != Extent)
{
std::terminate();
}
}

template <
class C,
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
std::declval<const C &>()))>::type (*)[],
T (*)[]>::value &&
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
size_t>::value>::type * = nullptr>
span(const C &c) noexcept(noexcept(nostd::data(c), nostd::size(c))) : data_{nostd::data(c)}
{
if (nostd::size(c) != Extent)
{
std::terminate();
}
}

template <class U,
size_t N,
typename std::enable_if<N == Extent &&
std::is_convertible<U (*)[], T (*)[]>::value>::type * = nullptr>
span(const span<U, N> &other) noexcept : data_{other.data()}
{}

span(const span &) noexcept = default;

bool empty() const noexcept { return Extent == 0; }

T *data() const noexcept { return data_; }

size_t size() const noexcept { return Extent; }

T &operator[](size_t index) const noexcept
{
assert(index < Extent);
return data_[index];
}

T *begin() const noexcept { return data_; }

T *end() const noexcept { return data_ + Extent; }

private:
T *data_;
};

template <class T>
class span<T, dynamic_extent>
{
public:
static constexpr size_t extent = dynamic_extent;

span() noexcept : extent_{0}, data_{nullptr} {}

span(T *data, size_t count) noexcept : extent_{count}, data_{data} {}

span(T *first, T *last) noexcept
: extent_{static_cast<size_t>(std::distance(first, last))}, data_{first}
{
assert(first <= last);
}

template <size_t N>
span(T (&array)[N]) noexcept : extent_{N}, data_{array}
{}

template <size_t N>
span(std::array<T, N> &array) noexcept : extent_{N}, data_{array.data()}
{}

template <size_t N>
span(const std::array<T, N> &array) noexcept : extent_{N}, data_{array.data()}
{}

template <
class C,
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
std::declval<C &>()))>::type (*)[],
T (*)[]>::value &&
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
size_t>::value>::type * = nullptr>
span(C &c) noexcept(noexcept(nostd::data(c), nostd::size(c)))
: extent_{nostd::size(c)}, data_{nostd::data(c)}
{}

template <
class C,
typename std::enable_if<!detail::is_specialized_span_convertible<C>::value &&
std::is_convertible<typename std::remove_pointer<decltype(nostd::data(
std::declval<const C &>()))>::type (*)[],
T (*)[]>::value &&
std::is_convertible<decltype(nostd::size(std::declval<const C &>())),
size_t>::value>::type * = nullptr>
span(const C &c) noexcept(noexcept(nostd::data(c), nostd::size(c)))
: extent_{nostd::size(c)}, data_{nostd::data(c)}
{}

template <class U,
size_t N,
typename std::enable_if<std::is_convertible<U (*)[], T (*)[]>::value>::type * = nullptr>
span(const span<U, N> &other) noexcept : extent_{other.size()}, data_{other.data()}
{}

span(const span &) noexcept = default;

bool empty() const noexcept { return extent_ == 0; }

T *data() const noexcept { return data_; }

size_t size() const noexcept { return extent_; }

T &operator[](size_t index) const noexcept
{
assert(index < extent_);
return data_[index];
}

T *begin() const noexcept { return data_; }

T *end() const noexcept { return data_ + extent_; }

private:
// Note: matches libstdc++'s layout for std::span
// See
// https://github.com/gcc-mirror/gcc/blob/a60701e05b3878000ff9fdde1aecbc472b9dec5a/libstdc%2B%2B-v3/include/std/span#L402-L403
size_t extent_;
T *data_;
};
} // namespace nostd
} // namespace opentelemetry
56 changes: 56 additions & 0 deletions api/include/opentelemetry/nostd/utility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include <cstddef>
#include <initializer_list>

namespace opentelemetry
{
namespace nostd
{
/**
* Back port of std::data
*
* See https://en.cppreference.com/w/cpp/iterator/data
*/
template <class C>
auto data(C &c) noexcept(noexcept(c.data())) -> decltype(c.data())
{
return c.data();
}

template <class C>
auto data(const C &c) noexcept(noexcept(c.data())) -> decltype(c.data())
{
return c.data();
}

template <class T, size_t N>
T *data(T (&array)[N]) noexcept
{
return array;
}

template <class E>
const E *data(std::initializer_list<E> list) noexcept
{
return list.begin();
}

/**
* Back port of std::size
*
* See https://en.cppreference.com/w/cpp/iterator/size
*/
template <class C>
auto size(const C &c) noexcept(noexcept(c.size())) -> decltype(c.size())
{
return c.size();
}

template <class T, size_t N>
size_t size(T (&array)[N]) noexcept
{
return N;
}
} // namespace nostd
} // namespace opentelemetry
22 changes: 22 additions & 0 deletions api/test/nostd/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,25 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "utility_test",
srcs = [
"utility_test.cc",
],
deps = [
"//api",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "span_test",
srcs = [
"span_test.cc",
],
deps = [
"//api",
"@com_google_googletest//:gtest_main",
],
)
10 changes: 10 additions & 0 deletions api/test/nostd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@ target_link_libraries(string_view_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET string_view_test TEST_PREFIX nostd. TEST_LIST
string_view_test)

add_executable(utility_test utility_test.cc)
target_link_libraries(utility_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET utility_test TEST_PREFIX nostd. TEST_LIST utility_test)

add_executable(span_test span_test.cc)
target_link_libraries(span_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET span_test TEST_PREFIX nostd. TEST_LIST span_test)
Loading

0 comments on commit c85b20b

Please sign in to comment.