diff --git a/test/test_span.cpp b/test/test_span.cpp index 5ac5cfec..c6e42566 100644 --- a/test/test_span.cpp +++ b/test/test_span.cpp @@ -8,8 +8,6 @@ #include -#define FIXED 10 - namespace test { using namespace PortsOfCall::span; @@ -72,29 +70,22 @@ constexpr bool contains(span s, span sub) { } // namespace detail -template -auto span_require(S &&s, I &&i) { - REQUIRE(s.size() == N); - REQUIRE(s.data() == i); - REQUIRE(s.begin() == i); - REQUIRE(s.end() == i + N); -} - TEST_CASE("span default construction", "[PortsOfCall::span]") { + static_assert(std::is_nothrow_default_constructible>::value, "span is not nothrow default constructible"); static_assert(std::is_nothrow_constructible>::value, "span is not nothrow constructible"); - static_assert(!std::is_nothrow_constructible>::value, - "span is nothrow construcible"); + static_assert(!std::is_nothrow_constructible>::value, + "span is nothrow construcible"); SECTION("dynamic size") { - constexpr span s{}; - static_assert(s.size() == 0, "default constructed dynamic size span has size != 0"); - static_assert(s.data() == 0, - "default constructed dynamic size span has data != nullptr"); - static_assert(s.begin() == s.end(), + constexpr span static_span; + static_assert(nullptr == static_span.data(), ""); + static_assert(0u == static_span.size(), ""); + + static_assert(static_span.begin() == static_span.end(), "default constructed dynamic size span has begin != end"); } @@ -116,144 +107,216 @@ TEST_CASE("span iter, extent construction", "[PortsOfCall::span]") { static_assert(std::is_constructible, const int *, int>::value, "span(const int*, int) is not constructible"); - static_assert(std::is_constructible, int *, int>::value, - "span(int*, int) is not constructible"); - static_assert(std::is_constructible, int *, int>::value, - "span(int*, int) is not constructible"); - static_assert(std::is_constructible, const int *, int>::value, - "span(const int*, int) is not constructible"); + static_assert(std::is_constructible, int *, int>::value, + "span(int*, int) is not constructible"); + static_assert(std::is_constructible, int *, int>::value, + "span(int*, int) is not constructible"); + static_assert(std::is_constructible, const int *, int>::value, + "span(const int*, int) is not constructible"); int arr[] = {0, 1, 2}; SECTION("dynamic") { span s{arr, arr + 3}; - span_require(s, arr); + REQUIRE(s.size() == 3); + REQUIRE(s.data() == arr); + REQUIRE(s.begin() == arr); + REQUIRE(s.end() == arr + 3); } SECTION("fixed") { span s(arr, 3); - span_require(s, arr); + REQUIRE(s.size() == 3); + REQUIRE(s.data() == arr); + REQUIRE(s.begin() == arr); + REQUIRE(s.end() == arr + 3); } } -template -constexpr auto array_construcible_correct() { - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(std::is_nothrow_constructible, const C &>::value, ""); - - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(std::is_nothrow_constructible, const C &>::value, ""); -} - -template -constexpr auto array_construcible_correct_oversized() { - constexpr auto M = N + 1; - static_assert(!std::is_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); - - static_assert(!std::is_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); -} - TEST_CASE("span Carr construction", "[PortsOfCall::span]") { - using int_array_t = int[3]; - using real_array_t = double[3]; - - array_construcible_correct(); - array_construcible_correct(); + using int_carr_t = int[3]; + static_assert(std::is_nothrow_constructible, int_carr_t &>::value, ""); + static_assert(!std::is_constructible, const int_carr_t &>::value, ""); + static_assert(std::is_nothrow_constructible, int_carr_t &>::value, ""); + static_assert(std::is_nothrow_constructible, const int_carr_t &>::value, + ""); - array_construcible_correct_oversized(); + static_assert(std::is_nothrow_constructible, int_carr_t &>::value, ""); + static_assert(!std::is_constructible, const int_carr_t &>::value, ""); + static_assert(std::is_nothrow_constructible, int_carr_t &>::value, + ""); + static_assert( + std::is_nothrow_constructible, const int_carr_t &>::value, ""); - int_array_t arr = {0, 1, 2}; + static_assert(!std::is_constructible, int_carr_t &>::value, ""); + static_assert(!std::is_constructible, const int_carr_t &>::value, ""); - SECTION("dynamic") { - span s{arr}; - span_require(s, arr); - } - - SECTION("dynamic const") { - span s{arr}; - span_require(s, arr); - } + static_assert(!std::is_constructible, int_carr_t &>::value, ""); + static_assert(!std::is_constructible, const int_carr_t &>::value, + ""); - SECTION("fixed") { - span s{arr}; - span_require(s, arr); + SECTION("regular c-array") { + int arr[] = {0, 1, 2}; + span const_span(arr); + REQUIRE(arr == const_span.data()); + REQUIRE(std::size(arr) == const_span.size()); + for (size_t i = 0; i < const_span.size(); ++i) + REQUIRE(arr[i] == const_span[i]); + span dynamic_span(arr); + REQUIRE(arr == dynamic_span.data()); + REQUIRE(std::size(arr) == dynamic_span.size()); + for (size_t i = 0; i < dynamic_span.size(); ++i) + REQUIRE(arr[i] == dynamic_span[i]); + span static_span(arr); + REQUIRE(arr == static_span.data()); + REQUIRE(std::size(arr) == static_span.size()); + for (size_t i = 0; i < static_span.size(); ++i) + REQUIRE(arr[i] == static_span[i]); } - SECTION("fixed const") { - span s{arr}; - span_require(s, arr); + SECTION("constexpr c-array") { + static constexpr int kArray[] = {5, 4, 3, 2, 1}; + constexpr span dynamic_span(kArray); + static_assert(kArray == dynamic_span.data(), ""); + static_assert(std::size(kArray) == dynamic_span.size(), ""); + static_assert(kArray[0] == dynamic_span[0], ""); + static_assert(kArray[1] == dynamic_span[1], ""); + static_assert(kArray[2] == dynamic_span[2], ""); + static_assert(kArray[3] == dynamic_span[3], ""); + static_assert(kArray[4] == dynamic_span[4], ""); + constexpr span static_span(kArray); + static_assert(kArray == static_span.data(), ""); + static_assert(std::size(kArray) == static_span.size(), ""); + static_assert(kArray[0] == static_span[0], ""); + static_assert(kArray[1] == static_span[1], ""); + static_assert(kArray[2] == static_span[2], ""); + static_assert(kArray[3] == static_span[3], ""); + static_assert(kArray[4] == static_span[4], ""); } } TEST_CASE("span std::array construction", "[PortsOfCall::span]") { - using int_array_t = std::array; - using real_array_t = std::array; - using zero_array_t = std::array; - - array_construcible_correct(); - array_construcible_correct(); - array_construcible_correct(); - - array_construcible_correct_oversized(); - array_construcible_correct_oversized(); - - int_array_t arr = {0, 1, 2}; - - SECTION("dynamic") { - span s{arr}; - span_require(s, arr.data()); - } - - SECTION("dynamic const") { - span s{arr}; - span_require(s, arr.data()); - } - - SECTION("fixed") { - span s{arr}; - span_require(s, arr.data()); - } - - SECTION("fixed const") { - span s{arr}; - span_require(s, arr.data()); - } -} - -template -constexpr auto container_construcible_correct() { - using E = typename C::value_type; - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); - static_assert(std::is_nothrow_constructible, C &>::value, ""); - static_assert(std::is_nothrow_constructible, const C &>::value, ""); - - static_assert(!std::is_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); - static_assert(!std::is_constructible, C &>::value, ""); - static_assert(!std::is_constructible, const C &>::value, ""); + // In the following assertions we use std::is_convertible_v, which + // for non-void types is equivalent to checking whether the following + // expression is well-formed: + // + // T obj = std::declval(); + // + // In particular we are checking whether From is implicitly convertible to To, + // which also implies that To is explicitly constructible from From. + + static_assert(std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be convertible to " + "span with dynamic extent."); + static_assert(std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be convertible to " + "span with the same static extent."); + static_assert(std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be convertible to " + "span with dynamic extent."); + static_assert(std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be convertible to " + "span with the same static extent."); + static_assert(std::is_convertible &, span>::value, + "Error: const l-value reference to std::array should be " + "convertible to span with dynamic extent."); + static_assert( + std::is_convertible &, span>::value, + "Error: const l-value reference to std::array should be convertible " + "to span with the same static extent."); + static_assert(std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be " + "convertible to span with dynamic extent."); + static_assert( + std::is_convertible &, span>::value, + "Error: l-value reference to std::array should be convertible " + "to span with the same static extent."); + static_assert( + std::is_convertible &, span>::value, + "Error: const l-value reference to std::array should be " + "convertible to span with dynamic extent."); + static_assert( + std::is_convertible &, span>::value, + "Error: const l-value reference to std::array should be " + "convertible to span with the same static extent."); + + // In the following assertions we use !std::is_constructible_v, which + // is equivalent to checking whether the following expression is malformed: + // + // T obj(std::declval()...); + // + // In particular we are checking that T is not explicitly constructible from + // Args, which also implies that T is not implicitly constructible from Args + // as well. + static_assert(!std::is_constructible, const std::array &>::value, + "Error: span with dynamic extent should not be " + "constructible from const l-value reference to std::array"); + static_assert(!std::is_constructible, std::array &>::value, + "Error: span with dynamic extent should not be constructible " + "from l-value reference to std::array"); + static_assert( + !std::is_constructible, const std::array &>::value, + "Error: span with dynamic extent should not be constructible " + "const from l-value reference to std::array"); + static_assert(!std::is_constructible, std::array &>::value, + "Error: span with static extent should not be constructible " + "from l-value reference to std::array with different extent"); + static_assert(!std::is_constructible, std::array &>::value, + "Error: span with dynamic extent should not be constructible " + "from l-value reference to std::array with different extent"); + static_assert(!std::is_constructible, std::array &>::value, + "Error: span with dynamic extent should not be constructible " + "from l-value reference to std::array"); + + // Note: Constructing a constexpr span from a constexpr std::array does not + // work prior to C++17 due to non-constexpr std::array::data. + std::array array = {{5, 4, 3, 2, 1}}; + span const_span(array); + REQUIRE(array.data() == const_span.data()); + REQUIRE(array.size() == const_span.size()); + for (size_t i = 0; i < const_span.size(); ++i) + REQUIRE(array[i] == const_span[i]); + span dynamic_span(array); + REQUIRE(array.data() == dynamic_span.data()); + REQUIRE(array.size() == dynamic_span.size()); + for (size_t i = 0; i < dynamic_span.size(); ++i) + REQUIRE(array[i] == dynamic_span[i]); + span static_span(array); + REQUIRE(array.data() == static_span.data()); + REQUIRE(array.size() == static_span.size()); + for (size_t i = 0; i < static_span.size(); ++i) + REQUIRE(array[i] == static_span[i]); } TEST_CASE("span container construction", "[PortsOfCall::span]") { using vec_t = std::vector; - container_construcible_correct(); + static_assert(std::is_nothrow_constructible, vec_t &>::value, ""); + static_assert(!std::is_constructible, const vec_t &>::value, ""); + static_assert(std::is_nothrow_constructible, vec_t &>::value, ""); + static_assert(std::is_nothrow_constructible, const vec_t &>::value, ""); + + static_assert(!std::is_constructible, vec_t &>::value, ""); + static_assert(!std::is_constructible, const vec_t &>::value, ""); + static_assert(!std::is_constructible, vec_t &>::value, ""); + static_assert(!std::is_constructible, const vec_t &>::value, ""); + vec_t varr = {1, 2, 3}; SECTION("dynamic") { span s{varr}; - span_require(s, varr.data()); + REQUIRE(s.size() == 3); + REQUIRE(s.data() == varr.data()); + REQUIRE(s.begin() == varr.data()); + REQUIRE(s.end() == varr.data() + 3); } SECTION("dynamic const") { span s{varr}; - span_require(s, varr.data()); + REQUIRE(s.size() == 3); + REQUIRE(s.data() == varr.data()); + REQUIRE(s.begin() == varr.data()); + REQUIRE(s.end() == varr.data() + 3); } // NB: fixed/fixed const is equivilant to std::array construction } @@ -351,7 +414,10 @@ TEST_CASE("span member subview operations", "[PortsOfCall::span]") { auto f = s.first<3>(); static_assert(std::is_same>::value, ""); - span_require(f, arr); + REQUIRE(f.size() == 3); + REQUIRE(f.data() == arr); + REQUIRE(f.begin() == arr); + REQUIRE(f.end() == arr + 3); } SECTION("last") { @@ -378,7 +444,10 @@ TEST_CASE("span member subview operations", "[PortsOfCall::span]") { auto f = s.first(3); static_assert(std::is_same>::value, ""); - span_require(f, arr); + REQUIRE(f.size() == 3); + REQUIRE(f.data() == arr); + REQUIRE(f.begin() == arr); + REQUIRE(f.end() == arr + 3); } SECTION("last(n)") {