diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 0eaeee7fa1..7f2b0ffe90 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -40,6 +40,8 @@ units wpimath/src/main/native/include/units/ Eigen wpimath/src/main/native/eigeninclude/ wpimath/src/main/native/include/unsupported/ StackWalker wpiutil/src/main/native/windows/StackWalker.* +TCB span wpiutil/src/main/native/include/wpi/span.h + wpiutil/src/test/native/cpp/span/ Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java wpilibc/src/main/native/include/spline/SplineParameterizer.h @@ -856,3 +858,30 @@ the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What the **** You Want to Public License, Version 2, as published by the WTFPL Task Force. See http://www.wtfpl.net/ for more details. + +====================== +Boost Software License +====================== +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 2edcfaad1a..c6dcfbc034 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -58,6 +58,8 @@ generatedFileExclude { src/main/native/include/wpi/iterator_range\.h$ src/main/native/include/wpi/raw_os_ostream\.h$ src/main/native/include/wpi/raw_ostream\.h$ + src/main/native/include/wpi/span\.h$ + src/test/native/cpp/span/ src/main/native/include/wpi/type_traits\.h$ src/main/native/cpp/json src/main/native/include/wpi/json diff --git a/wpiutil/src/main/native/include/wpi/span.h b/wpiutil/src/main/native/include/wpi/span.h new file mode 100644 index 0000000000..74aa17ef75 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/span.h @@ -0,0 +1,397 @@ + +/* +This is an implementation of C++20's std::span +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf +*/ + +// Copyright Tristan Brindle 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file ../../LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef WPIUTIL_WPI_SPAN_HPP_INCLUDED +#define WPIUTIL_WPI_SPAN_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace wpi { + +inline constexpr std::size_t dynamic_extent = SIZE_MAX; + +template +class span; + +namespace detail { + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept + : ptr(p_ptr) + {} + + E* ptr = nullptr; + static constexpr std::size_t size = S; +}; + +template +struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept + : ptr(p_ptr), size(p_size) + {} + + E* ptr = nullptr; + std::size_t size = 0; +}; + +template +using uncvref_t = + typename std::remove_cv::type>::type; + +template +struct is_span : std::false_type {}; + +template +struct is_span> : std::true_type {}; + +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +struct has_size_and_data : std::false_type {}; + +template +struct has_size_and_data())), + decltype(std::data(std::declval()))>> + : std::true_type {}; + +template > +struct is_container { + static constexpr bool value = + !is_span::value && !is_std_array::value && + !std::is_array::value && has_size_and_data::value; +}; + +template +using remove_pointer_t = typename std::remove_pointer::type; + +template +struct is_container_element_type_compatible : std::false_type {}; + +template +struct is_container_element_type_compatible< + T, E, + typename std::enable_if< + !std::is_same()))>::type, + void>::value>::type> + : std::is_convertible< + remove_pointer_t()))> (*)[], + E (*)[]> {}; + +template +struct is_complete : std::false_type {}; + +template +struct is_complete : std::true_type {}; + +} // namespace detail + +template +class span { + static_assert(std::is_object::value, + "A span's ElementType must be an object type (not a " + "reference type or void)"); + static_assert(detail::is_complete::value, + "A span's ElementType must be a complete type (not a forward " + "declaration)"); + static_assert(!std::is_abstract::value, + "A span's ElementType cannot be an abstract class type"); + + using storage_type = detail::span_storage; + +public: + // constants and types + using element_type = ElementType; + using value_type = typename std::remove_cv::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + + static constexpr size_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + template < + std::size_t E = Extent, + typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> + constexpr span() noexcept + {} + + constexpr span(pointer ptr, size_type count) + : storage_(ptr, count) + { + assert(extent == dynamic_extent || count == extent); + } + + constexpr span(pointer first_elem, pointer last_elem) + : storage_(first_elem, last_elem - first_elem) + { + assert(extent == dynamic_extent || + last_elem - first_elem == + static_cast(extent)); + } + + template ::value, + int>::type = 0> + constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) + {} + + template &, ElementType>::value, + int>::type = 0> + constexpr span(std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template &, ElementType>::value, + int>::type = 0> + constexpr span(const std::array& arr) noexcept + : storage_(arr.data(), N) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + Container&, ElementType>::value, + int>::type = 0> + constexpr span(Container& cont) + : storage_(std::data(cont), std::size(cont)) + {} + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value && + detail::is_container_element_type_compatible< + const Container&, ElementType>::value, + int>::type = 0> + constexpr span(const Container& cont) + : storage_(std::data(cont), std::size(cont)) + {} + + constexpr span(const span& other) noexcept = default; + + template ::value, + int>::type = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), other.size()) + {} + + ~span() noexcept = default; + + constexpr span& + operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const + { + assert(Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const + { + assert(Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + using subspan_return_t = + span; + + template + constexpr subspan_return_t subspan() const + { + assert(Offset <= size() && + (Count == dynamic_extent || Offset + Count <= size())); + return {data() + Offset, + Count != dynamic_extent ? Count : size() - Offset}; + } + + constexpr span + first(size_type count) const + { + assert(count <= size()); + return {data(), count}; + } + + constexpr span + last(size_type count) const + { + assert(count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span + subspan(size_type offset, size_type count = dynamic_extent) const + { + assert(offset <= size() && + (count == dynamic_extent || offset + count <= size())); + return {data() + offset, + count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr size_type size() const noexcept { return storage_.size; } + + constexpr size_type size_bytes() const noexcept + { + return size() * sizeof(element_type); + } + + [[nodiscard]] constexpr bool empty() const noexcept + { + return size() == 0; + } + + // [span.elem], span element access + constexpr reference operator[](size_type idx) const + { + assert(idx < size()); + return *(data() + idx); + } + + constexpr reference front() const + { + assert(!empty()); + return *data(); + } + + constexpr reference back() const + { + assert(!empty()); + return *(data() + (size() - 1)); + } + + constexpr pointer data() const noexcept { return storage_.ptr; } + + // [span.iterators], span iterator support + constexpr iterator begin() const noexcept { return data(); } + + constexpr iterator end() const noexcept { return data() + size(); } + + constexpr reverse_iterator rbegin() const noexcept + { + return reverse_iterator(end()); + } + + constexpr reverse_iterator rend() const noexcept + { + return reverse_iterator(begin()); + } + +private: + storage_type storage_{}; +}; + +/* Deduction Guides */ +template +span(T (&)[N])->span; + +template +span(std::array&)->span; + +template +span(const std::array&)->span; + +template +span(Container&)->span; + +template +span(const Container&)->span; + +template +span +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template < + class ElementType, size_t Extent, + typename std::enable_if::value, int>::type = 0> +span +as_writable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template +constexpr auto get(span s) -> decltype(s[N]) +{ + return s[N]; +} + +} // namespace wpi + +namespace std { + +template +class tuple_size> + : public integral_constant {}; + +template +class tuple_size>; // not defined + +template +class tuple_element> { +public: + static_assert(Extent != wpi::dynamic_extent && + I < Extent, + ""); + using type = ElementType; +}; + +} // end namespace std + +#endif // WPIUTIL_WPI_SPAN_HPP_INCLUDED diff --git a/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp b/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp new file mode 100644 index 0000000000..9851b5e948 --- /dev/null +++ b/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp @@ -0,0 +1,98 @@ + +#include "wpi/span.h" + +#include "gtest/gtest.h" + +#include + +using wpi::span; + +namespace { + +template +constexpr bool equal(R1&& r1, R2&& r2) +{ + auto first1 = std::begin(r1); + const auto last1 = std::end(r1); + auto first2 = std::begin(r2); + const auto last2 = std::end(r2); + + while (first1 != last1 && first2 != last2) { + if (*first1 != *first2) { + return false; + } + ++first1; + ++first2; + } + + return true; +} + +constexpr bool test_raw_array() +{ + int arr[] = {1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +constexpr bool test_const_raw_array() +{ + constexpr int arr[] = {1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +constexpr bool test_std_array() +{ + auto arr = std::array{1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +constexpr bool test_const_std_array() +{ + const auto arr = std::array{1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +bool test_vec() +{ + auto arr = std::vector{1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +bool test_const_vec() +{ + const auto arr = std::vector{1, 2, 3}; + auto s = span{arr}; + static_assert(std::is_same_v>); + + return equal(arr, s); +} + +} + +TEST(Deduction, FromRawArrays) +{ + static_assert(test_raw_array()); + static_assert(test_const_raw_array()); + static_assert(test_std_array()); + static_assert(test_const_std_array()); + + ASSERT_TRUE(test_std_array()); + ASSERT_TRUE(test_const_std_array()); + ASSERT_TRUE(test_vec()); + ASSERT_TRUE(test_const_vec()); +} diff --git a/wpiutil/src/test/native/cpp/span/test_span.cpp b/wpiutil/src/test/native/cpp/span/test_span.cpp new file mode 100644 index 0000000000..0e1fb8b4c3 --- /dev/null +++ b/wpiutil/src/test/native/cpp/span/test_span.cpp @@ -0,0 +1,662 @@ + +#include "wpi/span.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +using wpi::span; + +struct base {}; +struct derived : base {}; + +TEST(Span, DefaultConstruction) +{ + static_assert(std::is_nothrow_default_constructible>::value, ""); + static_assert(std::is_nothrow_default_constructible>::value, + ""); + static_assert(!std::is_default_constructible>::value, ""); + + //SECTION("dynamic size") + { + constexpr span s{}; + static_assert(s.size() == 0, ""); + static_assert(s.data() == nullptr, ""); + // This causes an ICE on MSVC +#ifndef _MSC_VER + static_assert(s.begin() == s.end(), ""); +#else + ASSERT_EQ(s.begin(), s.end()); +#endif + } + + //SECTION("fixed size") + { + constexpr span s{}; + static_assert(s.size() == 0, ""); + static_assert(s.data() == nullptr, ""); +#ifndef _MSC_VER + static_assert(s.begin() == s.end(), ""); +#else + ASSERT_EQ(s.begin(), s.end()); +#endif + } +} + +TEST(Span, PointerLengthConstruction) +{ + static_assert(std::is_constructible, int*, int>::value, ""); + static_assert(std::is_constructible, int*, int>::value, ""); + static_assert( + std::is_constructible, const int*, int>::value, ""); + + static_assert(std::is_constructible, int*, int>::value, ""); + static_assert(std::is_constructible, int*, int>::value, + ""); + static_assert( + std::is_constructible, const int*, int>::value, ""); + + //SECTION("dynamic size") + { + int arr[] = {1, 2, 3}; + span s(arr, 3); + + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } + + //SECTION("fixed size") + { + int arr[] = {1, 2, 3}; + span s(arr, 3); + + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } +} + +TEST(Span, PointerPointerConstruction) +{ + static_assert(std::is_constructible, int*, int*>::value, ""); + static_assert(!std::is_constructible, float*, float*>::value, ""); + static_assert(std::is_constructible, int*, int*>::value, ""); + static_assert(!std::is_constructible, float*, float*>::value, + ""); + + //SECTION("dynamic size") + { + int arr[] = {1, 2, 3}; + span s{arr, arr + 3}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } + + //SECTION("fixed size") + { + int arr[] = {1, 2, 3}; + span s{arr, arr + 3}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } +} + +TEST(Span, CArrayConstruction) +{ + using int_array_t = int[3]; + using float_array_t = float[3]; + + static_assert(std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(!std::is_constructible, int_array_t const&>::value, + ""); + static_assert(!std::is_constructible, float_array_t>::value, ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(std::is_nothrow_constructible, + int_array_t const&>::value, + ""); + static_assert(!std::is_constructible, float_array_t>::value, + ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, ""); + static_assert(!std::is_constructible, float_array_t&>::value, + ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(std::is_nothrow_constructible, + int_array_t const&>::value, + ""); + static_assert( + !std::is_constructible, float_array_t>::value, ""); + + static_assert(!std::is_constructible, int_array_t&>::value, + ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, ""); + static_assert(!std::is_constructible, float_array_t&>::value, + ""); + + static_assert( + !std::is_constructible, int_array_t&>::value, ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, + ""); + static_assert( + !std::is_constructible, float_array_t&>::value, ""); + + //SECTION("non-const, dynamic size") + { + int arr[] = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } + + //SECTION("const, dynamic size") + { + int arr[] = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } + + //SECTION("non-const, static size") + { + int arr[] = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } + + //SECTION("const, dynamic size") + { + int arr[] = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.begin(), std::begin(arr)); + ASSERT_EQ(s.end(), std::end(arr)); + } +} + +TEST(Span, StdArrayConstruction) +{ + using int_array_t = std::array; + using float_array_t = std::array; + using zero_array_t = std::array; + + static_assert(std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(!std::is_constructible, int_array_t const&>::value, + ""); + static_assert(!std::is_constructible, float_array_t>::value, ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(std::is_nothrow_constructible, + int_array_t const&>::value, + ""); + static_assert( + !std::is_constructible, float_array_t const&>::value, + ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, ""); + static_assert(!std::is_constructible, float_array_t>::value, + ""); + + static_assert( + std::is_nothrow_constructible, int_array_t&>::value, + ""); + static_assert(std::is_nothrow_constructible, + int_array_t const&>::value, + ""); + static_assert( + !std::is_constructible, float_array_t const&>::value, + ""); + + static_assert(!std::is_constructible, int_array_t&>::value, + ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, ""); + static_assert( + !std::is_constructible, float_array_t const&>::value, ""); + + static_assert( + !std::is_constructible, int_array_t&>::value, ""); + static_assert( + !std::is_constructible, int_array_t const&>::value, + ""); + static_assert( + !std::is_constructible, float_array_t&>::value, ""); + + static_assert(std::is_constructible, zero_array_t&>::value, ""); + static_assert(!std::is_constructible, const zero_array_t&>::value, + ""); + static_assert(std::is_constructible, zero_array_t&>::value, + ""); + static_assert( + std::is_constructible, const zero_array_t&>::value, ""); + + static_assert(std::is_constructible, zero_array_t&>::value, + ""); + static_assert( + !std::is_constructible, const zero_array_t&>::value, ""); + static_assert( + std::is_constructible, zero_array_t&>::value, ""); + static_assert( + std::is_constructible, const zero_array_t&>::value, + ""); + + //SECTION("non-const, dynamic size") + { + int_array_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("const, dynamic size") + { + int_array_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("non-const, static size") + { + int_array_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("const, dynamic size") + { + int_array_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } +} + +TEST(Span, ConstructionFromOtherContainers) +{ + using vec_t = std::vector; + using deque_t = std::deque; + + static_assert(std::is_constructible, vec_t&>::value, ""); + static_assert(!std::is_constructible, const vec_t&>::value, ""); + static_assert(!std::is_constructible, const deque_t&>::value, ""); + + static_assert(std::is_constructible, vec_t&>::value, ""); + static_assert(std::is_constructible, const vec_t&>::value, + ""); + static_assert( + !std::is_constructible, const deque_t&>::value, ""); + + static_assert(!std::is_constructible, vec_t&>::value, ""); + static_assert(!std::is_constructible, const vec_t&>::value, + ""); + static_assert(!std::is_constructible, const deque_t&>::value, + ""); + + static_assert(!std::is_constructible, vec_t&>::value, ""); + static_assert( + !std::is_constructible, const vec_t&>::value, ""); + static_assert( + !std::is_constructible, const deque_t&>::value, ""); + + // vector is not contiguous and cannot be converted to span + // Regression test for https://github.com/tcbrindle/span/issues/24 + static_assert( + !std::is_constructible, std::vector&>::value, ""); + static_assert(!std::is_constructible, + const std::vector&>::value, ""); + + //SECTION("non-const, dynamic size") + { + vec_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("const, dynamic size") + { + vec_t arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("non-const, static size") + { + std::array arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } + + //SECTION("const, dynamic size") + { + std::array arr = {1, 2, 3}; + span s{arr}; + ASSERT_EQ(s.size(), 3u); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.begin(), arr.data()); + ASSERT_EQ(s.end(), arr.data() + 3); + } +} + +TEST(Span, ConstructionFromSpansOfDifferentSize) +{ + using zero_span = span; + using zero_const_span = span; + using big_span = span; + using big_const_span = span; + using dynamic_span = span; + using dynamic_const_span = span; + + static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_move_constructible::value, ""); + static_assert(!std::is_constructible::value, + ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, + ""); + + static_assert( + std::is_nothrow_constructible::value, ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_move_constructible::value, + ""); + static_assert(!std::is_constructible::value, ""); + static_assert( + !std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, + ""); + static_assert( + !std::is_constructible::value, ""); + + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_move_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, + ""); + + static_assert(!std::is_constructible::value, ""); + static_assert( + !std::is_constructible::value, ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_move_constructible::value, + ""); + static_assert( + std::is_nothrow_constructible::value, ""); + static_assert(!std::is_constructible::value, + ""); + static_assert( + !std::is_constructible::value, ""); + + static_assert(std::is_nothrow_constructible::value, + ""); + static_assert(!std::is_constructible::value, + ""); + static_assert(std::is_nothrow_constructible::value, + ""); + static_assert(!std::is_constructible::value, + ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_move_constructible::value, + ""); + static_assert( + !std::is_constructible::value, ""); + + static_assert( + std::is_nothrow_constructible::value, + ""); + static_assert(std::is_nothrow_constructible::value, + ""); + static_assert( + std::is_nothrow_constructible::value, ""); + static_assert(std::is_nothrow_constructible::value, + ""); + static_assert( + std::is_nothrow_constructible::value, + ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert( + std::is_trivially_move_constructible::value, ""); + + constexpr zero_const_span s0{}; + constexpr dynamic_const_span d{s0}; + + static_assert(d.size() == 0, ""); + static_assert(d.data() == nullptr, ""); +#ifndef _MSC_VER + static_assert(d.begin() == d.end(), ""); +#else + ASSERT_EQ(d.begin(), d.end()); +#endif +} + +TEST(Span, MemberSubviewOperations) +{ + //SECTION("first") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto f = s.first<3>(); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(f.size(), 3u); + ASSERT_EQ(f.data(), arr); + ASSERT_EQ(f.begin(), arr); + ASSERT_EQ(f.end(), arr + 3); + } + + //SECTION("last") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto l = s.last<3>(); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(l.size(), 3u); + ASSERT_EQ(l.data(), arr + 2); + ASSERT_EQ(l.begin(), arr + 2); + ASSERT_EQ(l.end(), std::end(arr)); + } + + //SECTION("subspan") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto ss = s.subspan<1, 2>(); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(ss.size(), 2u); + ASSERT_EQ(ss.data(), arr + 1); + ASSERT_EQ(ss.begin(), arr + 1); + ASSERT_EQ(ss.end(), arr + 1 + 2); + } + + //SECTION("first(n)") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto f = s.first(3); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(f.size(), 3u); + ASSERT_EQ(f.data(), arr); + ASSERT_EQ(f.begin(), arr); + ASSERT_EQ(f.end(), arr + 3); + } + + //SECTION("last(n)") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto l = s.last(3); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(l.size(), 3u); + ASSERT_EQ(l.data(), arr + 2); + ASSERT_EQ(l.begin(), arr + 2); + ASSERT_EQ(l.end(), std::end(arr)); + } + + //SECTION("subspan(n)") + { + int arr[] = {1, 2, 3, 4, 5}; + span s{arr}; + auto ss = s.subspan(1, 2); + + static_assert(std::is_same>::value, ""); + ASSERT_EQ(ss.size(), 2u); + ASSERT_EQ(ss.data(), arr + 1); + ASSERT_EQ(ss.begin(), arr + 1); + ASSERT_EQ(ss.end(), arr + 1 + 2); + } + + // TODO: Test all the dynamic subspan possibilities +} + +TEST(Span, Observers) +{ + // We already use this everywhere, but whatever + constexpr span empty{}; + static_assert(empty.size() == 0, ""); + static_assert(empty.empty(), ""); + + constexpr int arr[] = {1, 2, 3}; + static_assert(span{arr}.size() == 3, ""); + static_assert(!span{arr}.empty(), ""); +} + +TEST(Span, ElementAccess) +{ + constexpr int arr[] = {1, 2, 3}; + span s{arr}; + + ASSERT_EQ(s[0], arr[0]); + ASSERT_EQ(s[1], arr[1]); + ASSERT_EQ(s[2], arr[2]); +} + +TEST(Span, IteratorSupport) +{ + { + std::vector vec; + span s{vec}; + std::sort(s.begin(), s.end()); + ASSERT_TRUE(std::is_sorted(vec.cbegin(), vec.cend())); + } + + { + const std::vector vec{1, 2, 3}; + span s{vec}; + ASSERT_TRUE(std::equal(s.rbegin(), s.rend(), vec.crbegin())); + } +} + +TEST(Span, MakeSpan) +{ + { + int arr[3] = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.size(), 3u); + } + + { + const int arr[3] = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr); + ASSERT_EQ(s.size(), 3u); + } + + { + std::array arr = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.size(), arr.size()); + } + + { + const std::array arr = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.size(), 3u); + } + + { + std::vector arr = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.size(), arr.size()); + } + + { + const std::vector arr = {1, 2, 3}; + auto s = wpi::span(arr); + static_assert(std::is_same>::value, ""); + ASSERT_EQ(s.data(), arr.data()); + ASSERT_EQ(s.size(), arr.size()); + } +} diff --git a/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp b/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp new file mode 100644 index 0000000000..5ce29693bc --- /dev/null +++ b/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp @@ -0,0 +1,30 @@ + +#include "wpi/span.h" + +#include "gtest/gtest.h" + +using static_span_t = wpi::span; +using dynamic_span_t = wpi::span; + +static_assert(std::tuple_size_v == static_span_t::extent); +static_assert(!wpi::detail::is_complete>::value); + +TEST(StructuredBindings, Test) +{ + // C++, why you no let me do constexpr structured bindings? + + int arr[] = {1, 2, 3}; + + auto& [a1, a2, a3] = arr; + auto&& [s1, s2, s3] = wpi::span(arr); + + ASSERT_EQ(a1, s1); + ASSERT_EQ(a2, s2); + ASSERT_EQ(a3, s3); + + a1 = 99; + ASSERT_EQ(s1, 99); + + s2 = 100; + ASSERT_EQ(a2, 100); +}