diff --git a/README.md b/README.md index 784aebc7..fbd17a0d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ span_p | & [cu32zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t` [**2. Owners**][cg-owners] | | stack_array | ☐ | A stack-allocated array -dyn_array | ☐ | A heap-allocated array +dyn_array | ☑ | A heap-allocated array [**3. Assertions**][cg-assertions] | | [Expects](docs/headers.md#user-content-H-assert-expects) | ☑ | A precondition assertion; on failure it terminates [Ensures](docs/headers.md#user-content-H-assert-ensures) | ☑ | A postcondition assertion; on failure it terminates diff --git a/docs/headers.md b/docs/headers.md index e8372f7c..9fd79d26 100644 --- a/docs/headers.md +++ b/docs/headers.md @@ -9,6 +9,7 @@ See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines - [``](#user-content-H-algorithms) - [``](#user-content-H-assert) - [``](#user-content-H-byte) +- [``](#user-content-H-dyn_array) - [``](#user-content-H-gsl) - [``](#user-content-H-narrow) - [``](#user-content-H-pointers) @@ -155,6 +156,10 @@ constexpr byte to_byte() noexcept; Convert the given value `I` to a `byte`. The template requires `I` to be in the valid range 0..255 for a `gsl::byte`. +## `` + +# TODO (@carsonradtke) + ## `` This header is a convenience header that includes all other [GSL headers](#user-content-H). diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array new file mode 100644 index 00000000..d9249f33 --- /dev/null +++ b/include/gsl/dyn_array @@ -0,0 +1,352 @@ +// -*- C++ -*- +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2026 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_DYN_ARRAY_H +#define GSL_DYN_ARRAY_H + +#include "./assert" +#include "./narrow" +#include "./util" + +#include +#include +#include + +#ifdef GSL_HAS_RANGES +#include +#endif /* GSL_HAS_RANGES */ + +namespace gsl +{ +namespace details +{ + template + struct dyn_array_traits + { + using value_type = T; + using pointer = T*; + using reference = T&; + using const_reference = const T&; + using difference_type = std::ptrdiff_t; + using size_type = decltype(sizeof(0)); + }; + + template > + class dyn_array_base : public Allocator + { + using pointer = typename dyn_array_traits::pointer; + using size_type = typename dyn_array_traits::size_type; + + const class dyn_array_impl + { + public: + constexpr dyn_array_impl(pointer data, size_type count) : _data{data}, _count{count} + { + Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); + } + + constexpr auto data() const { return _data; } + + constexpr auto count() const { return _count; } + + private: + pointer _data; + size_type _count; + } _impl; + + public: + constexpr dyn_array_base(const Allocator& alloc) : Allocator{alloc}, _impl{nullptr, 0} {} + constexpr dyn_array_base(size_type count, const Allocator& alloc) + : Allocator{alloc}, _impl{count == 0 ? nullptr : Allocator::allocate(count), count} + {} + + GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() + { + if (impl().data()) Allocator::deallocate(impl().data(), impl().count()); + } + + constexpr auto impl() const -> const dyn_array_impl& { return _impl; } + }; + + template + class dyn_array_iterator + { + using size_type = typename dyn_array_traits::size_type; + + public: + using difference_type = typename dyn_array_traits::difference_type; + using value_type = typename dyn_array_traits::value_type; + using pointer = typename dyn_array_traits::pointer; + using reference = typename dyn_array_traits::reference; + using const_reference = typename dyn_array_traits::const_reference; + using iterator_category = std::random_access_iterator_tag; + +#ifdef GSL_HAS_RANGES + constexpr dyn_array_iterator() : dyn_array_iterator(nullptr, 0, 0) {} +#endif /* GSL_HAS_RANGES */ + + constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos) + : _ptr{ptr}, _pos{pos}, _end_pos{end_pos} + { + Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0)); + Ensures(_pos <= _end_pos); + } + +#if defined(_MSC_VER) && defined(GSL_HAS_RANGES) + // TODO (@carsonradtke): Investigate why this is necessary for MSVC. + // Is it a a bug in GSL? STL? MSVC? + constexpr operator pointer() const { return _ptr + gsl::narrow(_pos); } +#endif /* defined(_MSC_VER) && defined(GSL_HAS_RANGES) */ + + constexpr auto operator==(const dyn_array_iterator& other) const + { + Expects(_ptr == other._ptr); + Expects(_end_pos == other._end_pos); + return _pos == other._pos; + } + + constexpr auto operator!=(const dyn_array_iterator& other) const + { + return !(*this == other); + } + + constexpr auto operator*() const -> reference + { + Expects(_ptr != nullptr); + Expects(_pos < _end_pos); + return _ptr[_pos]; + } + + constexpr auto operator++() -> dyn_array_iterator& + { + Expects(_pos < _end_pos); + _pos++; + return *this; + } + + constexpr auto operator++(int) + { + auto rv = *this; + ++(*this); + return rv; + } + + constexpr auto operator--() -> dyn_array_iterator& + { + Expects(_pos > 0); + _pos--; + return *this; + } + + constexpr auto operator--(int) + { + auto rv = *this; + --(*this); + return rv; + } + + constexpr auto operator+=(difference_type diff) -> dyn_array_iterator& + { + auto pos = gsl::narrow(_pos); + Expects(pos + diff <= gsl::narrow(_end_pos)); + _pos = gsl::narrow(pos + diff); + return *this; + } + + constexpr auto operator-=(difference_type diff) -> dyn_array_iterator& + { + auto pos = gsl::narrow(_pos); + Expects(pos >= diff); + _pos = gsl::narrow(pos - diff); + return *this; + } + + constexpr auto operator+(difference_type diff) const + { + return dyn_array_iterator{_ptr, _pos + gsl::narrow(diff), _end_pos}; + } + + constexpr auto operator-(difference_type diff) const + { + return dyn_array_iterator{_ptr, _pos + gsl::narrow(diff), _end_pos}; + } + + constexpr auto operator-(const dyn_array_iterator& other) const + { + Expects(_ptr == other._ptr); + Expects(_end_pos == other._end_pos); + return gsl::narrow(_pos) - gsl::narrow(other._pos); + } + + constexpr auto operator[](size_type pos) -> reference + { + Expects(_pos + pos < _end_pos); + return _ptr[_pos + pos]; + } + + constexpr auto operator[](size_type pos) const -> const_reference + { + return const_cast(*this).operator[](pos); + } + + private: + pointer _ptr; + size_type _pos; + size_type _end_pos; + }; +} // namespace details + +template > +class dyn_array : public details::dyn_array_base +{ + using base = details::dyn_array_base; + using pointer = typename details::dyn_array_traits::pointer; + +public: + using value_type = typename details::dyn_array_traits::value_type; + using reference = typename details::dyn_array_traits::reference; + using const_reference = typename details::dyn_array_traits::const_reference; + using iterator = details::dyn_array_iterator; + using const_iterator = details::dyn_array_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = typename details::dyn_array_traits::difference_type; + using size_type = typename details::dyn_array_traits::size_type; + + using allocator_type = Allocator; + + explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {} + + explicit constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) + : base{count, alloc} + { + std::fill(begin(), end(), value); + } + + GSL_TYPE_IS_ITERATOR(InputIt) + constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) + : base{gsl::narrow(std::distance(first, last)), alloc} + { + std::copy(first, last, begin()); + } + +#ifdef GSL_HAS_CONTAINER_RANGES + template + requires(std::ranges::input_range) + constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}) + : base{gsl::narrow(std::size(rg)), alloc} + { + std::ranges::copy(rg, std::ranges::begin(*this)); + } +#endif /* GSL_HAS_RANGES */ + + constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) + : dyn_array{count, T{}, alloc} + {} + + constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {}) + : dyn_array(other.begin(), other.end(), alloc) + {} + + constexpr dyn_array(std::initializer_list init, const Allocator& alloc = {}) + : dyn_array(init.begin(), init.end(), alloc) + {} + + constexpr auto operator=(const dyn_array& other) -> dyn_array& { return dyn_array{other}; } + + constexpr dyn_array(dyn_array&&) = delete; + dyn_array& operator=(dyn_array&&) = delete; + + constexpr auto operator==(const dyn_array& other) const + { + return size() == other.size() && std::equal(begin(), end(), other.begin(), other.end()); + } + + constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); } + + constexpr auto size() const { return impl().count(); } + + constexpr auto empty() const { return size() == 0; } + + constexpr auto max_size() const { return static_cast(-1); } + + constexpr auto get_allocator() -> Allocator& { return *this; } + + constexpr auto operator[](size_type pos) -> reference + { + Expects(pos < size()); + return data()[pos]; + } + + constexpr auto operator[](size_type pos) const -> const_reference + { + return const_cast(*this)[pos]; + } + + constexpr auto data() { return impl().data(); } + constexpr auto data() const -> const T* { return const_cast(*this).data(); } + + constexpr auto begin() { return iterator{data(), 0, size()}; } + constexpr auto begin() const { return const_iterator{data(), 0, size()}; } + + constexpr auto rbegin() { return reverse_iterator{end()}; } + constexpr auto rbegin() const { return const_reverse_iterator{end()}; } + +#ifdef _MSC_VER + constexpr auto _Unchecked_begin() { return data(); } + constexpr auto _Unchecked_begin() const -> const pointer + { + return const_cast(*this)._Unchecked_begin(); + } +#endif /* _MSC_VER */ + + constexpr auto end() { return iterator{data(), size(), size()}; } + constexpr auto end() const { return const_iterator{data(), size(), size()}; } + + constexpr auto rend() { return reverse_iterator{begin()}; } + constexpr auto rend() const { return const_reverse_iterator{begin()}; } + +#ifdef _MSC_VER + constexpr auto _Unchecked_end() { return data() + size(); } + constexpr auto _Unchecked_end() const -> const pointer + { + return const_cast(*this)._Unchecked_end(); + } +#endif /* MSC_VER */ + +private: + constexpr auto impl() const { return base::impl(); } +}; + +#ifdef GSL_HAS_DEDUCTION_GUIDES + +template ::value_type>> +dyn_array(InputIt, InputIt, + Alloc = {}) -> dyn_array::value_type, Alloc>; + +#ifdef GSL_HAS_CONTAINER_RANGES +template >> +dyn_array(std::from_range_t, InputRg&&, + Alloc = {}) -> dyn_array, Alloc>; +#endif /* GSL_HAS_RANGES */ + +#endif /* GSL_HAS_DEDUCTION_GUIDES */ +} // namespace gsl + +#endif /* defined(GSL_DYN_ARRAY_H) */ diff --git a/include/gsl/pointers b/include/gsl/pointers index 282229fe..ca8a2077 100644 --- a/include/gsl/pointers +++ b/include/gsl/pointers @@ -1,3 +1,4 @@ +// -*- C++ -*- /////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. @@ -338,7 +339,7 @@ auto make_strict_not_null(T&& t) noexcept return strict_not_null>>{std::forward(t)}; } -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#ifdef GSL_HAS_DEDUCTION_GUIDES // deduction guides to prevent the ctad-maybe-unsupported warning template @@ -346,7 +347,7 @@ not_null(T) -> not_null; template strict_not_null(T) -> strict_not_null; -#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) ) +#endif // GSL_HAS_DEDUCTION_GUIDES } // namespace gsl diff --git a/include/gsl/span b/include/gsl/span index cef8a774..ebad0c61 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -1,3 +1,4 @@ +// -*- C++ -*- /////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. @@ -767,7 +768,7 @@ private: } }; -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#ifdef GSL_HAS_DEDUCTION_GUIDES // Deduction Guides template @@ -787,7 +788,7 @@ template ().data())>> span(const Container&) -> span; -#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) ) +#endif // GSL_HAS_DEDUCTION_GUIDES #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) #if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) diff --git a/include/gsl/util b/include/gsl/util index 7a3caed4..0f46694a 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -1,3 +1,4 @@ +// -*- C++ -*- /////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. @@ -19,8 +20,8 @@ #include "./assert" // for Expects -#include #include // for ptrdiff_t, size_t +#include // for is_iterator, iterator_traits #include // for numeric_limits #include // for initializer_list #include // for is_signed, integral_constant @@ -86,6 +87,38 @@ #define GSL_DEPRECATED(msg) #endif // !defined(GSL_DEPRECATED) +#if (defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703L) +#define GSL_HAS_DEDUCTION_GUIDES +#endif // (defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703L) + +#if (defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L) +#define GSL_HAS_RANGES +#endif // (defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L) + +#if (defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L) +#define GSL_HAS_CONTAINER_RANGES +#endif // (defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L) + +#if (defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L) +#define GSL_HAS_CONCEPTS +#endif // (defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L) + +#if (defined(__cpp_lib_constexpr_dynamic_alloc) && __cpp_lib_constexpr_dynamic_alloc >= 201907L) +#define GSL_HAS_CONSTEXPR_ALLOCATOR +#endif // (defined(__cpp_lib_constexpr_dynamic_alloc) && __cpp_lib_constexpr_dynamic_alloc >= 201907L) + +#if __cplusplus >= 202002L +#define GSL_CONSTEXPR_SINCE_CPP20 constexpr +#define GSL_TYPE_IS_ITERATOR(Ty) \ + template \ + requires(std::input_iterator) +#else // ^^^ since C++20 /// before C++20 vvv +#define GSL_UNTIL_CPP20 +#define GSL_CONSTEXPR_SINCE_CPP20 +#define GSL_TYPE_IS_ITERATOR(Ty) \ + template ::value, bool> = true> +#endif // __cplusplus >= 202002L + namespace gsl { // @@ -95,6 +128,22 @@ namespace gsl // index type for all container indexes/subscripts/sizes using index = std::ptrdiff_t; +#ifdef GSL_UNTIL_CPP20 +namespace details +{ + template + using void_t = void; + + template + struct is_iterator : std::false_type + {}; + + template + struct is_iterator::value_type>> + : std::true_type {}; +} // namespace details +#endif /* GSL_UNTIL_CPP20 */ + // final_action allows you to ensure something gets run at the end of a scope template class final_action diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d583c24b..3ec624c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -151,6 +151,7 @@ else() -Wno-unknown-attributes -Wno-used-but-marked-unused # GTest EXPECT_DEATH -Wno-weak-vtables + -Wno-poison-system-directories $<$: # no support for [[maybe_unused]] -Wno-unused-member-function -Wno-unused-variable @@ -280,6 +281,7 @@ else() -Wno-missing-prototypes -Wno-unknown-attributes -Wno-weak-vtables + -Wno-poison-system-directories > $<$: -Wdouble-promotion # float implicit to double diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp new file mode 100644 index 00000000..8129c19b --- /dev/null +++ b/tests/dyn_array_tests.cpp @@ -0,0 +1,290 @@ +#include + +#include +#include + +// Despite using and utilities in this test, they +// are not being included directly by this file as a test to ensure +// transitive inclusion via . + +static_assert(sizeof(gsl::dyn_array) == 2 * sizeof(void*), + "gsl::dyn_array (with the default allocator) should be 16 bytes"); + +#ifdef GSL_HAS_CONCEPTS +static_assert(std::input_iterator::iterator>, + "gsl_dyn_array should expose a valid input_iterator"); +#endif /* GSL_HAS_CONCEPTS */ + +#ifdef GSL_HAS_RANGES +static_assert(std::ranges::input_range>, + "gsl::dyn_array should be a valid input range"); +#endif /* GSL_HAS_RANGES */ + +TEST(dyn_array_tests, default_ctor) +{ + gsl::dyn_array diamondbacks; + EXPECT_TRUE(diamondbacks.empty()); + EXPECT_EQ(diamondbacks.size(), 0); + EXPECT_EQ(diamondbacks.data(), nullptr); +} + +TEST(dyn_array_tests, count_ctor) +{ + gsl::dyn_array athletics(10); + EXPECT_FALSE(athletics.empty()); + EXPECT_EQ(athletics.size(), 10); + EXPECT_NE(athletics.data(), nullptr); + + gsl::dyn_array braves(0); + EXPECT_TRUE(braves.empty()); + EXPECT_EQ(braves.size(), 0); + EXPECT_EQ(braves.data(), nullptr); + EXPECT_TRUE(std::all_of(braves.begin(), braves.end(), [](char c) { return c == char{}; })); +} + +TEST(dyn_array_tests, count_value_ctor) +{ + gsl::dyn_array orioles(10, 'c'); + EXPECT_FALSE(orioles.empty()); + EXPECT_EQ(orioles.size(), 10); + EXPECT_NE(orioles.data(), nullptr); + EXPECT_TRUE(std::all_of(orioles.begin(), orioles.end(), [](char c) { return c == 'c'; })); + + gsl::dyn_array redsox(10, 42); + EXPECT_FALSE(redsox.empty()); + EXPECT_EQ(redsox.size(), 10); + EXPECT_NE(redsox.data(), nullptr); + EXPECT_TRUE(std::all_of(redsox.begin(), redsox.end(), [](int i) { return i == 42; })); +} + +TEST(dyn_array_tests, inputit_ctor) +{ + std::vector cubs(10, 'c'); + gsl::dyn_array whitesox(cubs.begin(), cubs.end()); + EXPECT_FALSE(whitesox.empty()); + EXPECT_EQ(whitesox.size(), cubs.size()); + EXPECT_NE(whitesox.data(), nullptr); + EXPECT_TRUE(std::all_of(whitesox.begin(), whitesox.end(), [](char c) { return c == 'c'; })); +} + +TEST(dyn_array_tests, copy_ctor) +{ + gsl::dyn_array reds(10, 'c'); + gsl::dyn_array guardians(reds); + EXPECT_FALSE(guardians.empty()); + EXPECT_EQ(guardians.size(), reds.size()); + EXPECT_NE(guardians.data(), nullptr); + EXPECT_TRUE(std::all_of(guardians.begin(), guardians.end(), [](char c) { return c == 'c'; })); +} + +TEST(dyn_array_tests, access_operator) +{ + gsl::dyn_array rockies(10, 'c'); + using ST = typename decltype(rockies)::size_type; + for (int i = 0; i < gsl::narrow(rockies.size()); i++) + EXPECT_EQ(rockies[gsl::narrow(i)], 'c'); + for (int i = 0; i < gsl::narrow(rockies.size()); i++) rockies[gsl::narrow(i)] = 'r'; + for (int i = 0; i < gsl::narrow(rockies.size()); i++) + EXPECT_EQ(rockies[gsl::narrow(i)], 'r'); + gsl::dyn_array tigers(10); + for (int i = 0; i < gsl::narrow(tigers.size()); i++) tigers[gsl::narrow(i)] = i; + for (int i = 0; i < gsl::narrow(tigers.size()); i++) + EXPECT_EQ(tigers[gsl::narrow(i)], i); +} + +TEST(dyn_array_tests, iterators) +{ + gsl::dyn_array astros(10, 'c'); + for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'c'); + for (auto it = astros.begin(); it != astros.end(); it++) *it = 'r'; + for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'r'); + EXPECT_TRUE(std::all_of(astros.begin(), astros.end(), [](char c) { return c == 'r'; })); + + gsl::dyn_array royals(10, 'c'); + for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'c'); + for (auto it = royals.begin(); it != royals.end(); ++it) *it = 'r'; + for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'r'); + EXPECT_TRUE(std::all_of(royals.begin(), royals.end(), [](char c) { return c == 'r'; })); +} + +TEST(dyn_array_tests, range_for) +{ + gsl::dyn_array angels(10, 'c'); + for (auto x : angels) EXPECT_EQ(x, 'c'); + for (auto& x : angels) x = 'r'; + for (auto x : angels) EXPECT_EQ(x, 'r'); + EXPECT_TRUE(std::all_of(angels.begin(), angels.end(), [](char c) { return c == 'r'; })); +} + +TEST(dyn_array_tests, use_std_algorithms) +{ + gsl::dyn_array dodgers(26); + std::generate(dodgers.begin(), dodgers.end(), [i = 0]() mutable { return 'a' + i++; }); + char ch = 'a'; + for (auto x : dodgers) EXPECT_EQ(x, ch++); + EXPECT_EQ(std::find(dodgers.begin(), dodgers.end(), 'a'), dodgers.begin()); + { + auto it = std::find(dodgers.begin(), dodgers.end(), 'c'); + EXPECT_EQ(std::distance(dodgers.begin(), it), 'c' - 'a'); + EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'c'); + } + { + auto it = std::lower_bound(dodgers.begin(), dodgers.end(), 'j'); + EXPECT_EQ(*it, 'j'); + EXPECT_EQ(std::distance(dodgers.begin(), it), 'j' - 'a'); + EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'j'); + } + EXPECT_EQ(dodgers.begin(), std::begin(dodgers)); + EXPECT_EQ(dodgers.end(), std::end(dodgers)); +} + +#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +TEST(dyn_array_tests, constexprness) +{ + constexpr gsl::dyn_array marlins; + static_assert(marlins == marlins); + static_assert(marlins.empty()); + static_assert(marlins.size() == 0); + static_assert(marlins.data() == nullptr); + static_assert(marlins.begin() == marlins.end()); + static_assert(std::distance(marlins.begin(), marlins.end()) == 0); +} +#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ + +#ifdef GSL_HAS_RANGES +TEST(dyn_array_tests, ranges) +{ + gsl::dyn_array brewers(26); + std::ranges::generate(brewers, [c = 'a']() mutable { return c++; }); + char ch = 'a'; + for (auto x : brewers) EXPECT_EQ(x, ch++); + EXPECT_EQ(std::ranges::find(brewers, 'a'), std::ranges::begin(brewers)); + { + auto it = std::ranges::find(brewers, 'c'); + EXPECT_EQ(std::ranges::distance(std::ranges::begin(brewers), it), 'c' - 'a'); + EXPECT_EQ(std::ranges::distance(it, std::ranges::begin(brewers)), 'a' - 'c'); + } + +#ifdef GSL_HAS_CONTAINER_RANGES + std::vector twins(10, 'c'); + gsl::dyn_array mets(std::from_range_t{}, twins); + EXPECT_EQ(twins.size(), mets.size()); + EXPECT_TRUE(std::ranges::all_of(mets, [](char c) { return c == 'c'; })); +#endif /* GSL_HAS_CONTAINER_RANGES */ +} +#endif /* GSL_HAS_RANGES */ + +#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +template +struct ConstexprAllocator +{ + T buf[N]; + std::size_t sz; + + constexpr ConstexprAllocator() : buf{}, sz{} {} + + constexpr auto allocate(std::size_t n) + { + auto addr = &buf[sz]; + sz += n; + return addr; + } + + constexpr void deallocate(T*, size_t) {} +}; +#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ + +template +static int AllocCounter = 0; + +template +static int DeallocCounter = 0; + +template +class Newocator +{ + static int allocations; + static int deallocations; + +public: + static void init() + { + AllocCounter> = 0; + DeallocCounter> = 0; + } + + static void check() { EXPECT_EQ(AllocCounter>, DeallocCounter>); } + + T* allocate(std::size_t n) + { + AllocCounter> ++; + return static_cast(new T[n]); + } + + void deallocate(T* p, size_t) + { + DeallocCounter> ++; + delete[] p; + } +}; + +TEST(dyn_array_tests, custom_allocator) +{ +#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR + static constexpr gsl::dyn_array> mets(10, 'c'); + static_assert(mets.size() == 10); + static_assert(mets[0] == 'c'); + static_assert(std::all_of(std::begin(mets), std::end(mets), [](char c) { return c == 'c'; })); +#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ + + Newocator::init(); + { + gsl::dyn_array> yankees(10, 'c'); + EXPECT_EQ(yankees.size(), 10); + EXPECT_TRUE( + std::all_of(std::begin(yankees), std::end(yankees), [](char c) { return c == 'c'; })); + yankees[0] = 'a'; + yankees[1] = 'b'; + EXPECT_EQ(yankees[0], 'a'); + EXPECT_EQ(yankees[1], 'b'); + EXPECT_EQ(yankees[2], 'c'); + yankees.get_allocator().deallocate(yankees.get_allocator().allocate(1), 1); + } + Newocator::check(); +} + +TEST(dyn_array_tests, init_list) +{ + gsl::dyn_array phillies = {'a', 'b', 'c'}; + EXPECT_EQ(phillies.size(), 3); + EXPECT_EQ(phillies[0], 'a'); + EXPECT_EQ(phillies[1], 'b'); + EXPECT_EQ(phillies[2], 'c'); +} + +TEST(dyn_array_tests, const_operations) +{ + const gsl::dyn_array pirates{'a', 'b', 'c', 'd'}; + EXPECT_EQ(pirates.size(), 4); + EXPECT_EQ(pirates[0], 'a'); +} + +TEST(dyn_array_tests, reverse_iterator) +{ + const gsl::dyn_array padres{'a', 'b', 'c'}; + auto it = std::rbegin(padres); + EXPECT_EQ(*it++, 'c'); + EXPECT_EQ(*it++, 'b'); + EXPECT_EQ(*it++, 'a'); +} + +#ifdef GSL_HAS_DEDUCTION_GUIDES +TEST(dyn_array_tests, deduction_guides) +{ + std::vector giants{10}; +#ifdef GSL_HAS_CONTAINER_RANGES + gsl::dyn_array mariners(std::from_range_t{}, giants); +#endif /* GSL_HAS_CONTAINER_RANGES */ + gsl::dyn_array cardinals(std::begin(giants), std::end(giants)); +} +#endif /* GSL_HAS_DEDUCTION_GUIDES */ diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 9b74ad44..aad88c53 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -32,14 +32,14 @@ #include // for vector // the string_view include and macro are used in the deduction guide verification -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#ifdef GSL_HAS_DEDUCTION_GUIDES #ifdef __has_include #if __has_include() #include #define HAS_STRING_VIEW #endif // __has_include() #endif // __has_include -#endif // (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#endif // GSL_HAS_DEDUCTION_GUIDES #if defined(__cplusplus) && __cplusplus >= 202002L #include #endif // __cplusplus >= 202002L @@ -1209,7 +1209,7 @@ TEST(span_test, default_constructible) TEST(span_test, std_container_ctad) { -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#ifdef GSL_HAS_DEDUCTION_GUIDES // this test is just to verify that these compile { std::vector v{1, 2, 3, 4}; @@ -1228,7 +1228,7 @@ TEST(span_test, std_container_ctad) static_assert(std::is_same>::value); } #endif -#endif +#endif // GSL_HAS_DEDUCTION_GUIDES } TEST(span_test, front_back) @@ -1294,13 +1294,13 @@ TEST(span_test, conversions) { int arr[5] = {1, 2, 3, 4, 5}; -#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) +#ifdef GSL_HAS_DEDUCTION_GUIDES span s = arr; span cs = s; -#else +#else // ^^^ deduction guides /// no deduction guides vvv span s = arr; span cs = s; -#endif +#endif // GSL_HAS_DEDUCTION_GUIDES EXPECT_TRUE(cs.size() == s.size()); EXPECT_TRUE(cs.data() == s.data());