From e590a2de48948ab5989b52b5e510d94195d04d41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:40:34 +0000 Subject: [PATCH 01/24] Initial plan From 8696c2c6ecc060a3a5dfb1a8b420fb3a8ceea459 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:45:17 +0000 Subject: [PATCH 02/24] Change generator from Unix Makefiles to Ninja for gcc and clang presets Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com> --- CMakePresets.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index eed32e24..2151d7ac 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -31,7 +31,7 @@ "name": "gcc-base", "inherits": "base", "hidden": true, - "generator": "Unix Makefiles", + "generator": "Ninja", "cacheVariables": { "CMAKE_CXX_COMPILER": "g++", "CMAKE_C_COMPILER": "gcc" @@ -41,7 +41,7 @@ "name": "clang-base", "inherits": "base", "hidden": true, - "generator": "Unix Makefiles", + "generator": "Ninja", "cacheVariables": { "CMAKE_CXX_COMPILER": "clang++", "CMAKE_C_COMPILER": "clang" From 06dd56dd33f6f7085e5ee0b51c9209f1ce320a0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:41:54 +0000 Subject: [PATCH 03/24] Use Ninja generator for msvc-base preset for consistency Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com> --- CMakePresets.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 2151d7ac..a3a599bf 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,14 +15,12 @@ "name": "msvc-base", "inherits": "base", "hidden": true, - "generator": "Visual Studio 17 2022", + "generator": "Ninja", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, - "toolset": "host=x64", - "architecture": "x64", "cacheVariables": { "CMAKE_CXX_COMPILER": "cl" } From 55f4956a2a7a736004ef94936cec838616a8922b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:27:57 +0000 Subject: [PATCH 04/24] Fix ClangCL builds by conditionally using Visual Studio generator Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com> --- .github/workflows/compilers.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 3ab6b5d9..e12c50aa 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -80,10 +80,9 @@ jobs: VisualStudio: strategy: matrix: - generator: [ 'Visual Studio 17 2022' ] image: [ windows-2022, windows-2025 ] build_type: [ Debug, Release ] - extra_args: [ '', '-T ClangCL' ] + toolset: [ '', 'ClangCL' ] cxx_version: [ 14, 17, 20, 23 ] runs-on: ${{ matrix.image }} steps: @@ -94,7 +93,7 @@ jobs: uses: ./.github/workflows/cmake with: cmake_preset: msvc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} - extra_cmake_configure_args: ${{ matrix.extra_args }} - extra_cmake_build_args: --config ${{ matrix.build_type }} - extra_ctest_args: -C ${{ matrix.build_type }} + extra_cmake_configure_args: ${{ matrix.toolset != '' && format('-G "Visual Studio 17 2022" -T {0}', matrix.toolset) || '' }} + extra_cmake_build_args: ${{ matrix.toolset != '' && format('--config {0}', matrix.build_type) || '' }} + extra_ctest_args: ${{ matrix.toolset != '' && format('-C {0}', matrix.build_type) || '' }} From bff804af79cac39cbc6a660cac7b0c4d1f2f524e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:28:57 +0000 Subject: [PATCH 05/24] Improve workflow readability with matrix include for generator configuration Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com> --- .github/workflows/compilers.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index e12c50aa..69996b39 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -84,6 +84,15 @@ jobs: build_type: [ Debug, Release ] toolset: [ '', 'ClangCL' ] cxx_version: [ 14, 17, 20, 23 ] + include: + # Regular MSVC builds use Ninja (from preset) + - toolset: '' + generator_override: '' + build_config_args: '' + test_config_args: '' + # ClangCL builds require Visual Studio generator + - toolset: 'ClangCL' + generator_override: '-G "Visual Studio 17 2022" -T ClangCL' runs-on: ${{ matrix.image }} steps: - uses: actions/checkout@v4 @@ -93,7 +102,7 @@ jobs: uses: ./.github/workflows/cmake with: cmake_preset: msvc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} - extra_cmake_configure_args: ${{ matrix.toolset != '' && format('-G "Visual Studio 17 2022" -T {0}', matrix.toolset) || '' }} - extra_cmake_build_args: ${{ matrix.toolset != '' && format('--config {0}', matrix.build_type) || '' }} - extra_ctest_args: ${{ matrix.toolset != '' && format('-C {0}', matrix.build_type) || '' }} + extra_cmake_configure_args: ${{ matrix.generator_override }} + extra_cmake_build_args: ${{ matrix.toolset == 'ClangCL' && format('--config {0}', matrix.build_type) || '' }} + extra_ctest_args: ${{ matrix.toolset == 'ClangCL' && format('-C {0}', matrix.build_type) || '' }} From ffdd653d73e7c5c2f35e8dfd544f495f74a4cb9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:30:01 +0000 Subject: [PATCH 06/24] Clean up unused matrix include fields Co-authored-by: carsonRadtke <10507970+carsonRadtke@users.noreply.github.com> --- .github/workflows/compilers.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 69996b39..b923a81a 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -88,8 +88,6 @@ jobs: # Regular MSVC builds use Ninja (from preset) - toolset: '' generator_override: '' - build_config_args: '' - test_config_args: '' # ClangCL builds require Visual Studio generator - toolset: 'ClangCL' generator_override: '-G "Visual Studio 17 2022" -T ClangCL' From 69a3a2190ffafa13c73af6d852014ad3b67c100a Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Tue, 27 Jan 2026 12:56:30 -0700 Subject: [PATCH 07/24] feat: implementation of gsl::dyn_array Implement gsl::dyn_array as specified by the CppCoreGuidlines here: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/dyn_array.md Currently the implementation is feature complete and all tests are passing under (at least) the following presets: - clang-14-debug - clang-17-debug - clang-20-debug - clang-23-debug (clang 18.1.3 on Ubuntu 24.04) (Apple Clang 17.0.0 on macOS Tahoe 26.3) - msvc-14-debug - msvc-17-debug - msvc-20-debug - msvc-23-debug (Visual Studio 2026 18.2 (toolset v145)) What is done: - [x] Basic Functionality - [x] Tests of basic functionality - [x] Tests for constexpr functions - [x] Tests/Support for ranges - [x] Ensure support for C++14, C++17, C++20, and C++23 - [x] Tests for custom allocators - [x] More constexpr tests using a constexpr allocator - [x] Tests for const iterators - [x] Sanity-check static_assertions to make sure gsl::dyn_array::iterator is a proper random-access iterator. - [x] Sanity-check static_assertions to make sure gsl::dyn_array is (mostly) a proper allocator-aware container - [x] Tests for construction of gsl::dyn_array from std::initializer_list - [x] Tests/Support for operations on const gsl::dyn_array - [x] Support for MSVC's unchecked iterators - [x] Tests/Support for reverse iterators - [x] Deduction guides for gsl::dyn_array What needs to be done: - [ ] Run dyn_array tests under ASAN - [ ] Ensure support for clang, gcc, MSVC - [ ] Create docs --- README.md | 2 +- docs/headers.md | 5 + include/gsl/dyn_array | 352 ++++++++++++++++++++++++++++++++++++++ include/gsl/pointers | 5 +- include/gsl/span | 5 +- include/gsl/util | 51 +++++- tests/CMakeLists.txt | 2 + tests/dyn_array_tests.cpp | 290 +++++++++++++++++++++++++++++++ tests/span_tests.cpp | 14 +- 9 files changed, 713 insertions(+), 13 deletions(-) create mode 100644 include/gsl/dyn_array create mode 100644 tests/dyn_array_tests.cpp 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 90649464..6de951b3 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 436f09a4..131757a6 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()); From 9f20a8c4458d99d1191d896c56ddeff169bcf070 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Fri, 6 Feb 2026 10:52:10 -0700 Subject: [PATCH 08/24] address a number of PR comments --- include/gsl/dyn_array | 48 ++++++++++++++++++++------------------- include/gsl/util | 6 ++--- tests/CMakeLists.txt | 4 ++++ tests/dyn_array_tests.cpp | 10 ++++---- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index d9249f33..22ce1a65 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -42,7 +42,7 @@ namespace details using reference = T&; using const_reference = const T&; using difference_type = std::ptrdiff_t; - using size_type = decltype(sizeof(0)); + using size_type = std::size_t; }; template > @@ -51,6 +51,7 @@ namespace details using pointer = typename dyn_array_traits::pointer; using size_type = typename dyn_array_traits::size_type; + protected: const class dyn_array_impl { public: @@ -76,10 +77,8 @@ namespace details GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() { - if (impl().data()) Allocator::deallocate(impl().data(), impl().count()); + if (_impl.data()) Allocator::deallocate(_impl.data(), _impl.count()); } - - constexpr auto impl() const -> const dyn_array_impl& { return _impl; } }; template @@ -96,7 +95,7 @@ namespace details using iterator_category = std::random_access_iterator_tag; #ifdef GSL_HAS_RANGES - constexpr dyn_array_iterator() : dyn_array_iterator(nullptr, 0, 0) {} + constexpr dyn_array_iterator() = default; #endif /* GSL_HAS_RANGES */ constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos) @@ -134,7 +133,7 @@ namespace details constexpr auto operator++() -> dyn_array_iterator& { Expects(_pos < _end_pos); - _pos++; + ++_pos; return *this; } @@ -148,7 +147,7 @@ namespace details constexpr auto operator--() -> dyn_array_iterator& { Expects(_pos > 0); - _pos--; + --_pos; return *this; } @@ -161,17 +160,19 @@ namespace details 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); + auto new_pos = gsl::narrow(_pos) + diff; + Expects(new_pos >= 0); + Expects(new_pos <= gsl::narrow(_end_pos)); + _pos = gsl::narrow(new_pos); return *this; } constexpr auto operator-=(difference_type diff) -> dyn_array_iterator& { - auto pos = gsl::narrow(_pos); - Expects(pos >= diff); - _pos = gsl::narrow(pos - diff); + auto new_pos = gsl::narrow(_pos) - diff; + Expects(new_pos >= 0); + Expects(new_pos <= gsl::narrow(_end_pos)); + _pos = gsl::narrow(new_pos); return *this; } @@ -204,14 +205,14 @@ namespace details } private: - pointer _ptr; - size_type _pos; - size_type _end_pos; + pointer _ptr{}; + size_type _pos{}; + size_type _end_pos{}; }; } // namespace details template > -class dyn_array : public details::dyn_array_base +class dyn_array : private details::dyn_array_base { using base = details::dyn_array_base; using pointer = typename details::dyn_array_traits::pointer; @@ -231,7 +232,7 @@ public: explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {} - explicit constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) + constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) : base{count, alloc} { std::fill(begin(), end(), value); @@ -278,7 +279,7 @@ public: constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); } - constexpr auto size() const { return impl().count(); } + constexpr auto size() const { return base::_impl.count(); } constexpr auto empty() const { return size() == 0; } @@ -297,14 +298,16 @@ public: return const_cast(*this)[pos]; } - constexpr auto data() { return impl().data(); } + constexpr auto data() { return base::_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 cbegin() const { return begin(); } constexpr auto rbegin() { return reverse_iterator{end()}; } constexpr auto rbegin() const { return const_reverse_iterator{end()}; } + constexpr auto crbegin() const { return rbegin(); } #ifdef _MSC_VER constexpr auto _Unchecked_begin() { return data(); } @@ -316,9 +319,11 @@ public: constexpr auto end() { return iterator{data(), size(), size()}; } constexpr auto end() const { return const_iterator{data(), size(), size()}; } + constexpr auto cend() const { return end(); } constexpr auto rend() { return reverse_iterator{begin()}; } constexpr auto rend() const { return const_reverse_iterator{begin()}; } + constexpr auto crend() const { return rend(); } #ifdef _MSC_VER constexpr auto _Unchecked_end() { return data() + size(); } @@ -327,9 +332,6 @@ public: return const_cast(*this)._Unchecked_end(); } #endif /* MSC_VER */ - -private: - constexpr auto impl() const { return base::impl(); } }; #ifdef GSL_HAS_DEDUCTION_GUIDES diff --git a/include/gsl/util b/include/gsl/util index 0f46694a..14c61516 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -113,7 +113,7 @@ template \ requires(std::input_iterator) #else // ^^^ since C++20 /// before C++20 vvv -#define GSL_UNTIL_CPP20 +#define GSL_BEFORE_CPP20 #define GSL_CONSTEXPR_SINCE_CPP20 #define GSL_TYPE_IS_ITERATOR(Ty) \ template ::value, bool> = true> @@ -128,7 +128,7 @@ namespace gsl // index type for all container indexes/subscripts/sizes using index = std::ptrdiff_t; -#ifdef GSL_UNTIL_CPP20 +#ifdef GSL_BEFORE_CPP20 namespace details { template @@ -142,7 +142,7 @@ namespace details struct is_iterator::value_type>> : std::true_type {}; } // namespace details -#endif /* GSL_UNTIL_CPP20 */ +#endif /* GSL_BEFORE_CPP20 */ // final_action allows you to ensure something gets run at the end of a scope template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ec624c1..b3284177 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -76,6 +76,10 @@ include(CheckCXXCompilerFlag) # please try to keep entries ordered =) add_library(gsl_tests_config INTERFACE) if(MSVC) # MSVC or simulating MSVC + set(CMAKE_VS_GLOBALS + "EnableMicrosoftCodeAnalysis=true" + "CodeAnalysisRuleSet=AllRules.ruleset" + ) target_compile_options(gsl_tests_config INTERFACE ${GSL_CPLUSPLUS_OPT} /EHsc diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 8129c19b..ce85c649 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -167,7 +167,7 @@ TEST(dyn_array_tests, ranges) #ifdef GSL_HAS_CONTAINER_RANGES std::vector twins(10, 'c'); - gsl::dyn_array mets(std::from_range_t{}, twins); + gsl::dyn_array mets(std::from_range, twins); EXPECT_EQ(twins.size(), mets.size()); EXPECT_TRUE(std::ranges::all_of(mets, [](char c) { return c == 'c'; })); #endif /* GSL_HAS_CONTAINER_RANGES */ @@ -178,10 +178,8 @@ TEST(dyn_array_tests, ranges) template struct ConstexprAllocator { - T buf[N]; - std::size_t sz; - - constexpr ConstexprAllocator() : buf{}, sz{} {} + T buf[N]{}; + std::size_t sz{}; constexpr auto allocate(std::size_t n) { @@ -283,7 +281,7 @@ TEST(dyn_array_tests, deduction_guides) { std::vector giants{10}; #ifdef GSL_HAS_CONTAINER_RANGES - gsl::dyn_array mariners(std::from_range_t{}, giants); + gsl::dyn_array mariners(std::from_range, giants); #endif /* GSL_HAS_CONTAINER_RANGES */ gsl::dyn_array cardinals(std::begin(giants), std::end(giants)); } From 9331f28e5fac853cff44b503678a256527639d8b Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Mon, 18 May 2026 11:35:14 -0600 Subject: [PATCH 09/24] update generator for vs18 development --- CMakePresets.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index eed32e24..d5db429d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -6,6 +6,7 @@ "hidden": true, "binaryDir": "${sourceDir}/build/${presetName}", "installDir": "${sourceDir}/install/${presetName}", + "generator": "Ninja", "cacheVariables": { "GSL_CXX_STANDARD": "14", "GSL_TEST": "ON" @@ -15,14 +16,11 @@ "name": "msvc-base", "inherits": "base", "hidden": true, - "generator": "Visual Studio 17 2022", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, - "toolset": "host=x64", - "architecture": "x64", "cacheVariables": { "CMAKE_CXX_COMPILER": "cl" } @@ -31,7 +29,6 @@ "name": "gcc-base", "inherits": "base", "hidden": true, - "generator": "Unix Makefiles", "cacheVariables": { "CMAKE_CXX_COMPILER": "g++", "CMAKE_C_COMPILER": "gcc" @@ -41,7 +38,6 @@ "name": "clang-base", "inherits": "base", "hidden": true, - "generator": "Unix Makefiles", "cacheVariables": { "CMAKE_CXX_COMPILER": "clang++", "CMAKE_C_COMPILER": "clang" From 135f3c2b52668de02b26c724aba4e0710e121491 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Tue, 19 May 2026 15:03:33 -0600 Subject: [PATCH 10/24] fold dyn_array_impl into dyn_array_base --- include/gsl/dyn_array | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 22ce1a65..5ce3d950 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -51,33 +51,31 @@ namespace details using pointer = typename dyn_array_traits::pointer; using size_type = typename dyn_array_traits::size_type; - protected: - 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; } + protected: + constexpr auto data() const { return _data; } - constexpr auto count() const { return _count; } + constexpr auto count() const { return _count; } - private: - pointer _data; - size_type _count; - } _impl; + private: + pointer _data; + size_type _count; public: - constexpr dyn_array_base(const Allocator& alloc) : Allocator{alloc}, _impl{nullptr, 0} {} + constexpr dyn_array_base(const Allocator& alloc) + : Allocator{alloc}, _data{nullptr}, _count{0} + { + Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); + } + constexpr dyn_array_base(size_type count, const Allocator& alloc) - : Allocator{alloc}, _impl{count == 0 ? nullptr : Allocator::allocate(count), count} - {} + : Allocator{alloc}, _data{count == 0 ? nullptr : Allocator::allocate(count)}, _count{count} + { + Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); + } GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() { - if (_impl.data()) Allocator::deallocate(_impl.data(), _impl.count()); + if (_data) Allocator::deallocate(_data, _count); } }; @@ -279,7 +277,7 @@ public: constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); } - constexpr auto size() const { return base::_impl.count(); } + constexpr auto size() const { return base::count(); } constexpr auto empty() const { return size() == 0; } @@ -298,7 +296,7 @@ public: return const_cast(*this)[pos]; } - constexpr auto data() { return base::_impl.data(); } + constexpr auto data() { return base::data(); } constexpr auto data() const -> const T* { return const_cast(*this).data(); } constexpr auto begin() { return iterator{data(), 0, size()}; } From 3505f5be07b8ee8cf07966ec0d9d9ed0ae3533b1 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Tue, 19 May 2026 15:06:10 -0600 Subject: [PATCH 11/24] remove dyn_array_traits and inline types --- include/gsl/dyn_array | 39 ++++++++++++++------------------------- tests/dyn_array_tests.cpp | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 5ce3d950..891faf26 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -34,22 +34,11 @@ 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 = std::size_t; - }; - template > class dyn_array_base : public Allocator { - using pointer = typename dyn_array_traits::pointer; - using size_type = typename dyn_array_traits::size_type; + using pointer = T*; + using size_type = std::size_t; protected: constexpr auto data() const { return _data; } @@ -82,14 +71,14 @@ namespace details template class dyn_array_iterator { - using size_type = typename dyn_array_traits::size_type; + using size_type = std::size_t; 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 difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + using const_reference = const T&; using iterator_category = std::random_access_iterator_tag; #ifdef GSL_HAS_RANGES @@ -213,18 +202,18 @@ template > class dyn_array : private details::dyn_array_base { using base = details::dyn_array_base; - using pointer = typename details::dyn_array_traits::pointer; + using pointer = T*; 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 value_type = T; + using reference = T&; + using const_reference = const T&; 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 difference_type = std::ptrdiff_t; + using size_type = std::size_t; using allocator_type = Allocator; diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index ce85c649..4c1895dc 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -2,6 +2,8 @@ #include #include +#include "gsl/dyn_array" +#include // Despite using and utilities in this test, they // are not being included directly by this file as a test to ensure @@ -276,6 +278,18 @@ TEST(dyn_array_tests, reverse_iterator) EXPECT_EQ(*it++, 'a'); } +TEST(DynArrayTests, TypeConsistency) +{ + static_assert(std::is_same_v::value_type, int>, "Value type mismatch"); + static_assert(std::is_same_v::reference, int&>, "Reference type mismatch"); + static_assert(std::is_same_v::const_reference, const int&>, "Const reference type mismatch"); + static_assert(std::is_same_v::iterator::value_type, int>, "Iterator value type mismatch"); + static_assert(std::is_same_v::iterator::reference, int&>, "Iterator reference type mismatch"); + static_assert(std::is_same_v::iterator::const_reference, const int&>, "Iterator const reference type mismatch"); + static_assert(std::is_same_v::size_type, std::size_t>, "Size type mismatch"); + static_assert(std::is_same_v::difference_type, std::ptrdiff_t>, "Difference type mismatch"); +} + #ifdef GSL_HAS_DEDUCTION_GUIDES TEST(dyn_array_tests, deduction_guides) { From b9ecae9b2b458c8124d19560347656993354ade0 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Tue, 19 May 2026 15:50:25 -0600 Subject: [PATCH 12/24] use allocator traits instead of digging into base allocator directly --- include/gsl/dyn_array | 8 +++- tests/dyn_array_tests.cpp | 92 ++++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 891faf26..68e0bd2b 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -57,14 +57,18 @@ namespace details } constexpr dyn_array_base(size_type count, const Allocator& alloc) - : Allocator{alloc}, _data{count == 0 ? nullptr : Allocator::allocate(count)}, _count{count} + : Allocator{alloc}, + _data{count == 0 ? nullptr : std::allocator_traits::allocate(static_cast(*this), count)}, + _count{count} { Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); } GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() { - if (_data) Allocator::deallocate(_data, _count); + if (_data) { + std::allocator_traits::deallocate(static_cast(*this), _data, _count); + } } }; diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 4c1895dc..6c5e03b4 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -180,18 +180,47 @@ TEST(dyn_array_tests, ranges) template struct ConstexprAllocator { + using value_type = T; + T buf[N]{}; std::size_t sz{}; - constexpr auto allocate(std::size_t n) + constexpr ConstexprAllocator() = default; + + template + constexpr ConstexprAllocator(const ConstexprAllocator&) noexcept + : buf{}, sz{} + {} + + template + struct rebind + { + using other = ConstexprAllocator; + }; + + constexpr auto allocate(std::size_t n) -> value_type* { auto addr = &buf[sz]; sz += n; return addr; } - constexpr void deallocate(T*, size_t) {} + constexpr void deallocate(value_type*, std::size_t) noexcept {} }; + +template +constexpr auto operator==(const ConstexprAllocator& lhs, + const ConstexprAllocator& rhs) noexcept +{ + return std::addressof(lhs) == std::addressof(rhs); +} + +template +constexpr auto operator!=(const ConstexprAllocator& lhs, + const ConstexprAllocator& rhs) noexcept +{ + return !(lhs == rhs); +} #endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ template @@ -203,10 +232,15 @@ static int DeallocCounter = 0; template class Newocator { - static int allocations; - static int deallocations; - public: + using value_type = T; + + Newocator() = default; + + template + Newocator(const Newocator&) noexcept + {} + static void init() { AllocCounter> = 0; @@ -215,19 +249,55 @@ class Newocator static void check() { EXPECT_EQ(AllocCounter>, DeallocCounter>); } - T* allocate(std::size_t n) + auto allocate(std::size_t n) -> value_type* { - AllocCounter> ++; - return static_cast(new T[n]); + AllocCounter>++; + return static_cast(::operator new(n * sizeof(value_type))); } - void deallocate(T* p, size_t) + void deallocate(value_type* p, std::size_t) noexcept { - DeallocCounter> ++; - delete[] p; + DeallocCounter>++; + ::operator delete(p); } + + template + struct rebind + { + using other = Newocator; + }; }; +template +constexpr auto operator==(const Newocator&, const Newocator&) noexcept +{ + return true; +} + +template +constexpr auto operator!=(const Newocator& lhs, const Newocator& rhs) noexcept +{ + return !(lhs == rhs); +} + +TEST(dyn_array_tests, custom_allocator_models_allocator) +{ + using traits = std::allocator_traits>; + using ptr = traits::pointer; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + Newocator alloc; + auto p = traits::allocate(alloc, 1); + traits::deallocate(alloc, p, 1); + +#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR + using constexpr_traits = std::allocator_traits>; + static_assert(std::is_same_v); +#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ +} + TEST(dyn_array_tests, custom_allocator) { #ifdef GSL_HAS_CONSTEXPR_ALLOCATOR From aad451d0d048727c7c6a1c2dd39db34367a434d4 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Tue, 19 May 2026 16:09:02 -0600 Subject: [PATCH 13/24] no more UB in fill and copy-fill constructors --- include/gsl/dyn_array | 66 ++++++++++++++++++++++++++++++++++++-- tests/dyn_array_tests.cpp | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 68e0bd2b..2133ff52 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef GSL_HAS_RANGES #include @@ -40,11 +41,67 @@ namespace details using pointer = T*; using size_type = std::size_t; + template + GSL_CONSTEXPR_SINCE_CPP20 void construct(pointer ptr, Args&&... args) + { + std::allocator_traits::construct(static_cast(*this), ptr, + std::forward(args)...); + } + + GSL_CONSTEXPR_SINCE_CPP20 void destroy(pointer ptr) + { + std::allocator_traits::destroy(static_cast(*this), ptr); + } + + GSL_CONSTEXPR_SINCE_CPP20 void destroy_range(pointer first, pointer last) + { + for (; first != last; ++first) { + destroy(first); + } + } + + GSL_CONSTEXPR_SINCE_CPP20 void rollback_construction(pointer first, pointer last) + { + destroy_range(first, last); + std::allocator_traits::deallocate(static_cast(*this), _data, _count); + _data = nullptr; + _count = 0; + } + protected: constexpr auto data() const { return _data; } constexpr auto count() const { return _count; } + GSL_CONSTEXPR_SINCE_CPP20 void fill(pointer first, size_type count, const T& value) + { + pointer current = first; + try { + for (size_type i = 0; i < count; ++i, ++current) { + construct(current, value); + } + } + catch (...) { + rollback_construction(first, current); + throw; + } + } + + template + GSL_CONSTEXPR_SINCE_CPP20 void copy(InputIt first, InputIt last, pointer output) + { + pointer current = output; + try { + for (; first != last; ++first, ++current) { + construct(current, *first); + } + } + catch (...) { + rollback_construction(output, current); + throw; + } + } + private: pointer _data; size_type _count; @@ -67,6 +124,9 @@ namespace details GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() { if (_data) { + if (!std::is_trivially_destructible::value) { + destroy_range(_data, _data + _count); + } std::allocator_traits::deallocate(static_cast(*this), _data, _count); } } @@ -226,14 +286,14 @@ public: constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) : base{count, alloc} { - std::fill(begin(), end(), value); + base::fill(data(), size(), 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()); + base::copy(first, last, data()); } #ifdef GSL_HAS_CONTAINER_RANGES @@ -242,7 +302,7 @@ public: 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)); + base::copy(std::ranges::begin(rg), std::ranges::end(rg), data()); } #endif /* GSL_HAS_RANGES */ diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 6c5e03b4..80a5c69d 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -229,6 +229,48 @@ static int AllocCounter = 0; template static int DeallocCounter = 0; +struct LifetimeCounter +{ + static int alive_count; + + int value{}; + + explicit LifetimeCounter(int v = 0) : value(v) { ++alive_count; } + + LifetimeCounter(const LifetimeCounter& other) : value(other.value) { ++alive_count; } + + ~LifetimeCounter() { --alive_count; } +}; + +int LifetimeCounter::alive_count = 0; + +struct ThrowOnCopy +{ + static int alive_count; + static int copy_count; + static int throw_on_copy_index; + + int value{}; + + explicit ThrowOnCopy(int v = 0) : value(v) { ++alive_count; } + + ThrowOnCopy(const ThrowOnCopy& other) : value(other.value) + { + if (copy_count == throw_on_copy_index) { + ++copy_count; + throw 42; + } + ++copy_count; + ++alive_count; + } + + ~ThrowOnCopy() { --alive_count; } +}; + +int ThrowOnCopy::alive_count = 0; +int ThrowOnCopy::copy_count = 0; +int ThrowOnCopy::throw_on_copy_index = -1; + template class Newocator { @@ -323,6 +365,31 @@ TEST(dyn_array_tests, custom_allocator) Newocator::check(); } +TEST(dyn_array_tests, non_trivial_elements_are_destroyed) +{ + LifetimeCounter::alive_count = 0; + + { + gsl::dyn_array values(5, LifetimeCounter{7}); + EXPECT_EQ(values.size(), 5); + EXPECT_EQ(LifetimeCounter::alive_count, 5); + } + + EXPECT_EQ(LifetimeCounter::alive_count, 0); +} + +TEST(dyn_array_tests, failed_element_construction_rolls_back) +{ + ThrowOnCopy::alive_count = 0; + ThrowOnCopy::copy_count = 0; + ThrowOnCopy::throw_on_copy_index = 2; + + EXPECT_THROW((gsl::dyn_array(5, ThrowOnCopy{1})), int); + EXPECT_EQ(ThrowOnCopy::alive_count, 0); + + ThrowOnCopy::throw_on_copy_index = -1; +} + TEST(dyn_array_tests, init_list) { gsl::dyn_array phillies = {'a', 'b', 'c'}; From e83986544e75a990c194c41368a177f76bd183b7 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 12:12:02 -0600 Subject: [PATCH 14/24] copilot found some bugs. added tests for them and fixed them. --- include/gsl/dyn_array | 48 +++++++++++++++++++--- include/gsl/util | 20 ++++----- tests/dyn_array_tests.cpp | 86 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 16 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 2133ff52..c68d2bcd 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -68,11 +68,21 @@ namespace details _count = 0; } - protected: + protected: constexpr auto data() const { return _data; } constexpr auto count() const { return _count; } + void resize(size_type count) { + // This should only be called when constructing a non-forward iteartor. + // It neither frees nor copies `_data`. + Expects(_data == nullptr && _count == 0); + if (count != 0) { + _data = std::allocator_traits::allocate(static_cast(*this), count); + _count = count; + } + } + GSL_CONSTEXPR_SINCE_CPP20 void fill(pointer first, size_type count, const T& value) { pointer current = first; @@ -102,6 +112,15 @@ namespace details } } + void swap(dyn_array_base& other) { + auto old_count = _count; + auto old_data = _data; + _count = other._count; + _data = other._data; + other._count = old_count; + other._data = old_data; + } + private: pointer _data; size_type _count; @@ -234,7 +253,7 @@ namespace details constexpr auto operator-(difference_type diff) const { - return dyn_array_iterator{_ptr, _pos + gsl::narrow(diff), _end_pos}; + return dyn_array_iterator{_ptr, _pos - gsl::narrow(diff), _end_pos}; } constexpr auto operator-(const dyn_array_iterator& other) const @@ -268,6 +287,11 @@ class dyn_array : private details::dyn_array_base using base = details::dyn_array_base; using pointer = T*; + constexpr void swap(dyn_array& other) { + // TODO (@carsonradtke): This was not in the spec, should it have been? + base::swap(other); + } + public: using value_type = T; using reference = T&; @@ -289,13 +313,21 @@ public: base::fill(data(), size(), value); } - GSL_TYPE_IS_ITERATOR(InputIt) + template ::value, bool> = true> constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) : base{gsl::narrow(std::distance(first, last)), alloc} { base::copy(first, last, data()); } + template ::value && details::is_iterator::value, bool> = true> + constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc={}) : dyn_array{alloc} { + // TODO (@carsonradtke): Can this be done better? + std::vector tmp(first, last); + base::resize(tmp.size()); + base::copy(std::begin(tmp), std::end(tmp), data()); + } + #ifdef GSL_HAS_CONTAINER_RANGES template requires(std::ranges::input_range) @@ -318,7 +350,11 @@ public: : dyn_array(init.begin(), init.end(), alloc) {} - constexpr auto operator=(const dyn_array& other) -> dyn_array& { return dyn_array{other}; } + constexpr auto operator=(const dyn_array& other) -> dyn_array& { + auto tmp = dyn_array{other}; + swap(tmp); + return *this; + } constexpr dyn_array(dyn_array&&) = delete; dyn_array& operator=(dyn_array&&) = delete; @@ -362,7 +398,7 @@ public: #ifdef _MSC_VER constexpr auto _Unchecked_begin() { return data(); } - constexpr auto _Unchecked_begin() const -> const pointer + constexpr auto _Unchecked_begin() const -> const T* { return const_cast(*this)._Unchecked_begin(); } @@ -378,7 +414,7 @@ public: #ifdef _MSC_VER constexpr auto _Unchecked_end() { return data() + size(); } - constexpr auto _Unchecked_end() const -> const pointer + constexpr auto _Unchecked_end() const -> const T* { return const_cast(*this)._Unchecked_end(); } diff --git a/include/gsl/util b/include/gsl/util index 14c61516..9b4c7b04 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -109,14 +109,9 @@ #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_BEFORE_CPP20 #define GSL_CONSTEXPR_SINCE_CPP20 -#define GSL_TYPE_IS_ITERATOR(Ty) \ - template ::value, bool> = true> #endif // __cplusplus >= 202002L namespace gsl @@ -128,21 +123,26 @@ namespace gsl // index type for all container indexes/subscripts/sizes using index = std::ptrdiff_t; -#ifdef GSL_BEFORE_CPP20 namespace details { template using void_t = void; template - struct is_iterator : std::false_type - {}; + struct is_iterator : std::false_type {}; - template + template struct is_iterator::value_type>> : std::true_type {}; + + template + struct is_fwd_iterator : std::false_type + {}; + + template + struct is_fwd_iterator::iterator_category>> + : std::integral_constant::iterator_category>::value> {}; } // namespace details -#endif /* GSL_BEFORE_CPP20 */ // final_action allows you to ensure something gets run at the end of a scope template diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 80a5c69d..792d7999 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -1,8 +1,13 @@ #include +#include "deathTestCommon.h" #include #include #include "gsl/dyn_array" +#include +#include +#include +#include #include // Despite using and utilities in this test, they @@ -415,6 +420,87 @@ TEST(dyn_array_tests, reverse_iterator) EXPECT_EQ(*it++, 'a'); } +TEST(dyn_array_tests, copy_assignment) +{ + gsl::dyn_array rays(3, 'r'); + const gsl::dyn_array rangers{'a', 'b', 'c'}; + + auto& assigned = (rays = rangers); + + EXPECT_EQ(&assigned, &rays); + EXPECT_EQ(rays.size(), rangers.size()); + EXPECT_EQ(rays[0], 'a'); + EXPECT_EQ(rays[1], 'b'); + EXPECT_EQ(rays[2], 'c'); +} + +TEST(dyn_array_tests, random_access_iterator_arithmetic) +{ + gsl::dyn_array bluejays{'a', 'b', 'c', 'd'}; + + auto first = bluejays.begin(); + auto third = first + 2; + + EXPECT_EQ(*third, 'c'); + EXPECT_EQ(third - first, 2); + EXPECT_EQ(*(third - 1), 'b'); + EXPECT_EQ(*std::prev(third), 'b'); +} + +TEST(dyn_array_tests, input_iterator_constructor) +{ + std::istringstream stream{"n a t s"}; + std::istream_iterator first{stream}; + const std::istream_iterator last{}; + + gsl::dyn_array nationals(first, last); + + ASSERT_EQ(nationals.size(), 4); + EXPECT_EQ(nationals[0], 'n'); + EXPECT_EQ(nationals[1], 'a'); + EXPECT_EQ(nationals[2], 't'); + EXPECT_EQ(nationals[3], 's'); +} + +TEST(dyn_array_tests, contract_violations) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. dyn_array_contract_violations"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + gsl::dyn_array values(3, 'v'); + gsl::dyn_array other(3, 'o'); + + EXPECT_DEATH(values[values.size()], expected); + EXPECT_DEATH((void) *values.end(), expected); + EXPECT_DEATH(++values.end(), expected); + EXPECT_DEATH(--values.begin(), expected); + EXPECT_DEATH((void) (values.begin() == other.begin()), expected); +} + +#ifdef _MSC_VER +TEST(dyn_array_tests, unchecked_iterators) +{ + gsl::dyn_array values; + const gsl::dyn_array const_values(3, 'v'); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + std::size_t count = 0; + for (const auto value : const_values) { + EXPECT_EQ(value, 'v'); + ++count; + } + EXPECT_EQ(count, const_values.size()); + + EXPECT_EQ(values._Unchecked_begin(), nullptr); + EXPECT_EQ(values._Unchecked_end(), nullptr); +} +#endif /* _MSC_VER */ + TEST(DynArrayTests, TypeConsistency) { static_assert(std::is_same_v::value_type, int>, "Value type mismatch"); From c71e7999fc65bf360cb19771fb49be92f9de26fa Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 12:34:09 -0600 Subject: [PATCH 15/24] fix cpp14 build and test failures (msvc) --- include/gsl/dyn_array | 2 +- include/gsl/util | 2 +- tests/dyn_array_tests.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index c68d2bcd..51071c94 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -351,7 +351,7 @@ public: {} constexpr auto operator=(const dyn_array& other) -> dyn_array& { - auto tmp = dyn_array{other}; + dyn_array tmp{other}; swap(tmp); return *this; } diff --git a/include/gsl/util b/include/gsl/util index 9b4c7b04..d03ed141 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -21,7 +21,7 @@ #include "./assert" // for Expects #include // for ptrdiff_t, size_t -#include // for is_iterator, iterator_traits +#include // for iterator_traits #include // for numeric_limits #include // for initializer_list #include // for is_signed, integral_constant diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 792d7999..445406f2 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -332,8 +332,8 @@ TEST(dyn_array_tests, custom_allocator_models_allocator) using traits = std::allocator_traits>; using ptr = traits::pointer; - static_assert(std::is_same_v); - static_assert(std::is_same_v); + static_assert(std::is_same::value, "allocator trait type mismatch"); + static_assert(std::is_same::value, "allocator trait type mismatch"); Newocator alloc; auto p = traits::allocate(alloc, 1); From 2a4f6dbfb52804331a7d3f802b900a867abd4c2e Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 12:37:46 -0600 Subject: [PATCH 16/24] is_same_v -> is_same + comments for static_assertion --- tests/dyn_array_tests.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 445406f2..fb171652 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -341,7 +341,7 @@ TEST(dyn_array_tests, custom_allocator_models_allocator) #ifdef GSL_HAS_CONSTEXPR_ALLOCATOR using constexpr_traits = std::allocator_traits>; - static_assert(std::is_same_v); + static_assert(std::is_same::value, "allocator trait type mismatch"); #endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ } @@ -503,14 +503,14 @@ TEST(dyn_array_tests, unchecked_iterators) TEST(DynArrayTests, TypeConsistency) { - static_assert(std::is_same_v::value_type, int>, "Value type mismatch"); - static_assert(std::is_same_v::reference, int&>, "Reference type mismatch"); - static_assert(std::is_same_v::const_reference, const int&>, "Const reference type mismatch"); - static_assert(std::is_same_v::iterator::value_type, int>, "Iterator value type mismatch"); - static_assert(std::is_same_v::iterator::reference, int&>, "Iterator reference type mismatch"); - static_assert(std::is_same_v::iterator::const_reference, const int&>, "Iterator const reference type mismatch"); - static_assert(std::is_same_v::size_type, std::size_t>, "Size type mismatch"); - static_assert(std::is_same_v::difference_type, std::ptrdiff_t>, "Difference type mismatch"); + static_assert(std::is_same::value_type, int>::value, "Value type mismatch"); + static_assert(std::is_same::reference, int&>::value, "Reference type mismatch"); + static_assert(std::is_same::const_reference, const int&>::value, "Const reference type mismatch"); + static_assert(std::is_same::iterator::value_type, int>::value, "Iterator value type mismatch"); + static_assert(std::is_same::iterator::reference, int&>::value, "Iterator reference type mismatch"); + static_assert(std::is_same::iterator::const_reference, const int&>::value, "Iterator const reference type mismatch"); + static_assert(std::is_same::size_type, std::size_t>::value, "Size type mismatch"); + static_assert(std::is_same::difference_type, std::ptrdiff_t>::value, "Difference type mismatch"); } #ifdef GSL_HAS_DEDUCTION_GUIDES From 36195f1f09a39d15713a4668ac1fa75b0ed66302 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 12:47:05 -0600 Subject: [PATCH 17/24] clang-cl support w/ ninja --- .github/workflows/compilers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index e87eddad..21ff1cda 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -83,7 +83,7 @@ jobs: generator: [ 'Visual Studio 17 2022' ] image: [ windows-2022, windows-2025 ] build_type: [ Debug, Release ] - extra_args: [ '', '-T ClangCL' ] + extra_args: [ '', '-DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl' ] cxx_version: [ 14, 17, 20, 23 ] runs-on: ${{ matrix.image }} steps: From 4ced9d66b7e49f7cfeb13d35814455bc7ea960ca Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 13:23:25 -0600 Subject: [PATCH 18/24] make sure we have a dev shell before running cmake --- .github/workflows/compilers.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 4c160bf3..da9b25ee 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -95,6 +95,7 @@ jobs: steps: - uses: actions/checkout@v6 - uses: microsoft/setup-msbuild@v3 + - uses: ilammy/msvc-dev-cmd@v1 - name: Run CMake (configure, build, test) uses: ./.github/workflows/cmake From 4e695e7f068c317e968d2c6cbca8e6e215247b6e Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 15:38:35 -0600 Subject: [PATCH 19/24] add tests and patch more issues found by gpt-5.5 --- include/gsl/dyn_array | 26 ++++-- tests/dyn_array_tests.cpp | 175 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 5 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 51071c94..9bf23310 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -112,6 +112,19 @@ namespace details } } + void default_construct(pointer first, size_type count) { + pointer current = first; + try { + for (size_type i = 0; i < count; ++i, ++current) { + construct(current); + } + } + catch (...) { + rollback_construction(first, current); + throw; + } + } + void swap(dyn_array_base& other) { auto old_count = _count; auto old_data = _data; @@ -248,12 +261,13 @@ namespace details constexpr auto operator+(difference_type diff) const { - return dyn_array_iterator{_ptr, _pos + gsl::narrow(diff), _end_pos}; + auto new_pos = gsl::narrow(_pos) + diff; + return dyn_array_iterator{_ptr, gsl::narrow(new_pos), _end_pos}; } constexpr auto operator-(difference_type diff) const { - return dyn_array_iterator{_ptr, _pos - gsl::narrow(diff), _end_pos}; + return *this + (-diff); } constexpr auto operator-(const dyn_array_iterator& other) const @@ -339,8 +353,10 @@ public: #endif /* GSL_HAS_RANGES */ constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) - : dyn_array{count, T{}, alloc} - {} + : base{count, alloc} + { + base::default_construct(data(), size()); + } constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {}) : dyn_array(other.begin(), other.end(), alloc) @@ -351,7 +367,7 @@ public: {} constexpr auto operator=(const dyn_array& other) -> dyn_array& { - dyn_array tmp{other}; + dyn_array tmp{other, get_allocator()}; swap(tmp); return *this; } diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index fb171652..45b47dc7 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -249,6 +249,39 @@ struct LifetimeCounter int LifetimeCounter::alive_count = 0; +struct DefaultConstructionCounter +{ + static int default_constructor_count; + static int copy_constructor_count; + + int value{}; + + DefaultConstructionCounter() { ++default_constructor_count; } + + DefaultConstructionCounter(const DefaultConstructionCounter& other) : value(other.value) + { + ++copy_constructor_count; + } + + static void reset() + { + default_constructor_count = 0; + copy_constructor_count = 0; + } +}; + +int DefaultConstructionCounter::default_constructor_count = 0; +int DefaultConstructionCounter::copy_constructor_count = 0; + +struct DefaultOnlyElement +{ + DefaultOnlyElement() = default; + DefaultOnlyElement(const DefaultOnlyElement&) = delete; + DefaultOnlyElement& operator=(const DefaultOnlyElement&) = delete; + DefaultOnlyElement(DefaultOnlyElement&&) = delete; + DefaultOnlyElement& operator=(DefaultOnlyElement&&) = delete; +}; + struct ThrowOnCopy { static int alive_count; @@ -327,6 +360,95 @@ constexpr auto operator!=(const Newocator& lhs, const Newocator& rhs) noex return !(lhs == rhs); } +template +class OwnershipTrackingAllocator +{ +public: + using value_type = T; + + OwnershipTrackingAllocator() noexcept : owner_id(next_owner_id()) { ++next_owner_id(); } + + explicit OwnershipTrackingAllocator(int owner) noexcept : owner_id(owner) {} + + template + OwnershipTrackingAllocator(const OwnershipTrackingAllocator& other) noexcept + : owner_id(other.owner()) + {} + + auto allocate(std::size_t count) -> value_type* + { + static_assert(alignof(value_type) <= alignof(int), + "test allocator only supports types with int-or-smaller alignment"); + auto raw = static_cast(::operator new(sizeof(int) + count * sizeof(value_type))); + *reinterpret_cast(raw) = owner_id; + ++allocation_count(); + return reinterpret_cast(raw + sizeof(int)); + } + + void deallocate(value_type* pointer, std::size_t) noexcept + { + auto raw = reinterpret_cast(pointer) - sizeof(int); + if (*reinterpret_cast(raw) != owner_id) { + ++mismatched_deallocation_count(); + } + ++deallocation_count(); + ::operator delete(raw); + } + + auto owner() const noexcept { return owner_id; } + + static void reset() + { + next_owner_id() = 1; + allocation_count() = 0; + deallocation_count() = 0; + mismatched_deallocation_count() = 0; + } + + static auto mismatched_deallocations() { return mismatched_deallocation_count(); } + +private: + int owner_id; + + static auto next_owner_id() -> int& + { + static int value = 1; + return value; + } + + static auto allocation_count() -> int& + { + static int value = 0; + return value; + } + + static auto deallocation_count() -> int& + { + static int value = 0; + return value; + } + + static auto mismatched_deallocation_count() -> int& + { + static int value = 0; + return value; + } +}; + +template +constexpr auto operator==(const OwnershipTrackingAllocator& lhs, + const OwnershipTrackingAllocator& rhs) noexcept +{ + return lhs.owner() == rhs.owner(); +} + +template +constexpr auto operator!=(const OwnershipTrackingAllocator& lhs, + const OwnershipTrackingAllocator& rhs) noexcept +{ + return !(lhs == rhs); +} + TEST(dyn_array_tests, custom_allocator_models_allocator) { using traits = std::allocator_traits>; @@ -370,6 +492,23 @@ TEST(dyn_array_tests, custom_allocator) Newocator::check(); } +TEST(dyn_array_tests, copy_assignment_deallocates_with_the_allocator_that_owns_the_storage) +{ + using allocator_type = OwnershipTrackingAllocator; + using array_type = gsl::dyn_array; + + allocator_type::reset(); + + { + array_type source(3, 's', allocator_type{1}); + array_type target(2, 't', allocator_type{2}); + + target = source; + } + + EXPECT_EQ(allocator_type::mismatched_deallocations(), 0); +} + TEST(dyn_array_tests, non_trivial_elements_are_destroyed) { LifetimeCounter::alive_count = 0; @@ -383,6 +522,27 @@ TEST(dyn_array_tests, non_trivial_elements_are_destroyed) EXPECT_EQ(LifetimeCounter::alive_count, 0); } +TEST(dyn_array_tests, count_constructor_default_constructs_each_element) +{ + DefaultConstructionCounter::reset(); + + { + gsl::dyn_array values(4); + EXPECT_EQ(values.size(), 4); + } + + EXPECT_EQ(DefaultConstructionCounter::default_constructor_count, 4); + EXPECT_EQ(DefaultConstructionCounter::copy_constructor_count, 0); +} + +#ifdef GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS +TEST(dyn_array_compile_failure_tests, count_constructor_accepts_default_constructible_only_elements) +{ + gsl::dyn_array values(4); + EXPECT_EQ(values.size(), 4); +} +#endif /* GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS */ + TEST(dyn_array_tests, failed_element_construction_rolls_back) { ThrowOnCopy::alive_count = 0; @@ -447,6 +607,21 @@ TEST(dyn_array_tests, random_access_iterator_arithmetic) EXPECT_EQ(*std::prev(third), 'b'); } +TEST(dyn_array_tests, random_access_iterator_arithmetic_accepts_negative_offsets) +{ + gsl::dyn_array bluejays{'a', 'b', 'c', 'd'}; + + auto third = bluejays.begin() + 2; + char previous{}; + char next{}; + + EXPECT_NO_THROW(previous = *(third + -1)); + EXPECT_EQ(previous, 'b'); + + EXPECT_NO_THROW(next = *(third - -1)); + EXPECT_EQ(next, 'd'); +} + TEST(dyn_array_tests, input_iterator_constructor) { std::istringstream stream{"n a t s"}; From cb8672945852a956dbdd43aa153a38566223faee Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 15:54:20 -0600 Subject: [PATCH 20/24] make sure helper functions are properly marked constexpr --- include/gsl/dyn_array | 6 +++--- tests/dyn_array_tests.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 9bf23310..0a1913b4 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -73,7 +73,7 @@ namespace details constexpr auto count() const { return _count; } - void resize(size_type count) { + GSL_CONSTEXPR_SINCE_CPP20 void resize(size_type count) { // This should only be called when constructing a non-forward iteartor. // It neither frees nor copies `_data`. Expects(_data == nullptr && _count == 0); @@ -112,7 +112,7 @@ namespace details } } - void default_construct(pointer first, size_type count) { + GSL_CONSTEXPR_SINCE_CPP20 void default_construct(pointer first, size_type count) { pointer current = first; try { for (size_type i = 0; i < count; ++i, ++current) { @@ -125,7 +125,7 @@ namespace details } } - void swap(dyn_array_base& other) { + constexpr void swap(dyn_array_base& other) { auto old_count = _count; auto old_data = _data; _count = other._count; diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 45b47dc7..15af0f38 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -146,6 +146,22 @@ TEST(dyn_array_tests, use_std_algorithms) } #ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +constexpr auto default_constructed_count_dyn_array_is_constexpr() +{ + gsl::dyn_array values(3); + return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0; +} + +constexpr auto copy_assigned_dyn_array_is_constexpr() +{ + gsl::dyn_array source(3, 7); + gsl::dyn_array target(2, 4); + + target = source; + + return target.size() == 3 && target[0] == 7 && target[1] == 7 && target[2] == 7; +} + TEST(dyn_array_tests, constexprness) { constexpr gsl::dyn_array marlins; @@ -155,6 +171,8 @@ TEST(dyn_array_tests, constexprness) static_assert(marlins.data() == nullptr); static_assert(marlins.begin() == marlins.end()); static_assert(std::distance(marlins.begin(), marlins.end()) == 0); + static_assert(default_constructed_count_dyn_array_is_constexpr()); + static_assert(copy_assigned_dyn_array_is_constexpr()); } #endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ From f76cfab4c114e816ff91c428ffcc7b85556ab31a Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 16:00:38 -0600 Subject: [PATCH 21/24] fix some typos --- include/gsl/dyn_array | 10 +++++----- tests/dyn_array_tests.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 0a1913b4..c03cf07f 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -74,7 +74,7 @@ namespace details constexpr auto count() const { return _count; } GSL_CONSTEXPR_SINCE_CPP20 void resize(size_type count) { - // This should only be called when constructing a non-forward iteartor. + // This should only be called when constructing a non-forward iterator. // It neither frees nor copies `_data`. Expects(_data == nullptr && _count == 0); if (count != 0) { @@ -190,7 +190,7 @@ namespace details #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? + // Is it a bug in GSL? STL? MSVC? constexpr operator pointer() const { return _ptr + gsl::narrow(_pos); } #endif /* defined(_MSC_VER) && defined(GSL_HAS_RANGES) */ @@ -350,7 +350,7 @@ public: { base::copy(std::ranges::begin(rg), std::ranges::end(rg), data()); } -#endif /* GSL_HAS_RANGES */ +#endif /* GSL_HAS_CONTAINER_RANGES */ constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) : base{count, alloc} @@ -434,7 +434,7 @@ public: { return const_cast(*this)._Unchecked_end(); } -#endif /* MSC_VER */ +#endif /* _MSC_VER */ }; #ifdef GSL_HAS_DEDUCTION_GUIDES @@ -449,7 +449,7 @@ template >> dyn_array(std::from_range_t, InputRg&&, Alloc = {}) -> dyn_array, Alloc>; -#endif /* GSL_HAS_RANGES */ +#endif /* GSL_HAS_CONTAINER_RANGES */ #endif /* GSL_HAS_DEDUCTION_GUIDES */ } // namespace gsl diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 15af0f38..438a2b34 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -19,7 +19,7 @@ static_assert(sizeof(gsl::dyn_array) == 2 * sizeof(void*), #ifdef GSL_HAS_CONCEPTS static_assert(std::input_iterator::iterator>, - "gsl_dyn_array should expose a valid input_iterator"); + "gsl::dyn_array should expose a valid input_iterator"); #endif /* GSL_HAS_CONCEPTS */ #ifdef GSL_HAS_RANGES From 63acb93ee87ff3bf07a0ac9f8a879ed4dac31719 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 16:33:33 -0600 Subject: [PATCH 22/24] resolve remaining todos --- docs/headers.md | 170 +++++++++++++++++++++++++++++++++++++++++- include/gsl/dyn_array | 4 - 2 files changed, 169 insertions(+), 5 deletions(-) diff --git a/docs/headers.md b/docs/headers.md index 6de951b3..dc83331a 100644 --- a/docs/headers.md +++ b/docs/headers.md @@ -158,7 +158,175 @@ Convert the given value `I` to a `byte`. The template requires `I` to be in the ## `` -# TODO (@carsonradtke) +This header contains an owning dynamically allocated array type whose size is fixed between assignments. + +- [`gsl::dyn_array`](#user-content-H-dyn_array-dyn_array) + +### `gsl::dyn_array` + +```cpp +template > +class dyn_array; +``` + +`gsl::dyn_array` owns a contiguous sequence of `T` objects allocated with `Allocator`. +The number of elements is established when the object is constructed and remains unchanged until the object is copy-assigned. +It provides bounds-checked element access and checked random-access iterators. + +`gsl::dyn_array` is useful when the number of elements is known only at runtime, but the array should not grow or shrink through container operations. + +#### Member Types + +```cpp +using value_type = T; +using reference = T&; +using const_reference = const T&; +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 = std::ptrdiff_t; +using size_type = std::size_t; + +using allocator_type = Allocator; +``` + +#### Member functions + +##### Construct/Copy + +```cpp +explicit constexpr dyn_array(const Allocator& alloc = {}); +``` + +Constructs an empty `dyn_array`. +No elements are allocated and `data()` returns `nullptr`. + +```cpp +constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}); + +constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` with `count` elements using `alloc`. +The first overload default-constructs each element. +The second overload constructs each element as a copy of `value`. + +```cpp +template +constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements in the range `[first, last)`. + +```cpp +template +constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements in `rg`. +This overload is available when container ranges are supported. + +```cpp +constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {}); + +constexpr dyn_array(std::initializer_list init, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements from another `dyn_array` or from an initializer list. + +```cpp +constexpr auto operator=(const dyn_array& other) -> dyn_array&; + +constexpr dyn_array(dyn_array&&) = delete; +dyn_array& operator=(dyn_array&&) = delete; +``` + +Copy assignment replaces the contents with copies of the elements in `other`. +Move construction and move assignment are explicitly deleted. + +##### Observers + +```cpp +constexpr auto size() const; +constexpr auto empty() const; +constexpr auto max_size() const; +constexpr auto get_allocator() -> Allocator&; +``` + +Returns the number of elements, whether the array is empty, the maximum representable size, or the allocator used by the `dyn_array`. + +##### Element access + +```cpp +constexpr auto operator[](size_type pos) -> reference; +constexpr auto operator[](size_type pos) const -> const_reference; +``` + +Returns a reference to the element at the given index. +[`Expects`](#user-content-H-assert-expects) that `pos` is less than the `dyn_array`'s size. + +```cpp +constexpr auto data(); +constexpr auto data() const -> const T*; +``` + +Returns a pointer to the beginning of the contained data. +If the `dyn_array` is empty, this returns `nullptr`. + +##### Iterators + +```cpp +constexpr auto begin(); +constexpr auto begin() const; +constexpr auto cbegin() const; + +constexpr auto end(); +constexpr auto end() const; +constexpr auto cend() const; +``` + +Returns an iterator to the first element or to one past the last element. + +```cpp +constexpr auto rbegin(); +constexpr auto rbegin() const; +constexpr auto crbegin() const; + +constexpr auto rend(); +constexpr auto rend() const; +constexpr auto crend() const; +``` + +Returns a reverse iterator to the first element of the reversed range or to one past the last element of the reversed range. + +The iterators are random-access iterators and perform bounds checking. +Dereferencing `end()`, moving before `begin()` or past `end()`, or comparing iterators from different arrays violates preconditions. + +##### Comparisons + +```cpp +constexpr auto operator==(const dyn_array& other) const; +constexpr auto operator!=(const dyn_array& other) const; +``` + +Compares two `dyn_array`s by size and element value. + +#### Deduction guides + +```cpp +template ::value_type>> +dyn_array(InputIt, InputIt, + Alloc = {}) -> dyn_array::value_type, Alloc>; + +template >> +dyn_array(std::from_range_t, InputRg&&, + Alloc = {}) -> dyn_array, Alloc>; +``` + +The range deduction guide is available when container ranges are supported. ## `` diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index c03cf07f..538c84b5 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -189,8 +189,6 @@ namespace details } #if defined(_MSC_VER) && defined(GSL_HAS_RANGES) - // TODO (@carsonradtke): Investigate why this is necessary for MSVC. - // Is it a bug in GSL? STL? MSVC? constexpr operator pointer() const { return _ptr + gsl::narrow(_pos); } #endif /* defined(_MSC_VER) && defined(GSL_HAS_RANGES) */ @@ -302,7 +300,6 @@ class dyn_array : private details::dyn_array_base using pointer = T*; constexpr void swap(dyn_array& other) { - // TODO (@carsonradtke): This was not in the spec, should it have been? base::swap(other); } @@ -336,7 +333,6 @@ public: template ::value && details::is_iterator::value, bool> = true> constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc={}) : dyn_array{alloc} { - // TODO (@carsonradtke): Can this be done better? std::vector tmp(first, last); base::resize(tmp.size()); base::copy(std::begin(tmp), std::end(tmp), data()); From 40d3ec1475c39a785eb000cf837f27ee9d4bb977 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Wed, 20 May 2026 16:37:28 -0600 Subject: [PATCH 23/24] remove GSL_HAS_* macros --- include/gsl/dyn_array | 24 +++++++++++------------ include/gsl/pointers | 4 ++-- include/gsl/span | 4 ++-- include/gsl/util | 20 -------------------- tests/dyn_array_tests.cpp | 40 +++++++++++++++++++-------------------- tests/span_tests.cpp | 12 ++++++------ 6 files changed, 42 insertions(+), 62 deletions(-) diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index 538c84b5..cd71b445 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -27,9 +27,9 @@ #include #include -#ifdef GSL_HAS_RANGES +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) #include -#endif /* GSL_HAS_RANGES */ +#endif /* __cpp_lib_ranges >= 201911L */ namespace gsl { @@ -177,9 +177,9 @@ namespace details using const_reference = const T&; using iterator_category = std::random_access_iterator_tag; -#ifdef GSL_HAS_RANGES +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) constexpr dyn_array_iterator() = default; -#endif /* GSL_HAS_RANGES */ +#endif /* __cpp_lib_ranges >= 201911L */ constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos) : _ptr{ptr}, _pos{pos}, _end_pos{end_pos} @@ -188,9 +188,9 @@ namespace details Ensures(_pos <= _end_pos); } -#if defined(_MSC_VER) && defined(GSL_HAS_RANGES) +#if defined(_MSC_VER) && defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) constexpr operator pointer() const { return _ptr + gsl::narrow(_pos); } -#endif /* defined(_MSC_VER) && defined(GSL_HAS_RANGES) */ +#endif /* defined(_MSC_VER) && __cpp_lib_ranges >= 201911L */ constexpr auto operator==(const dyn_array_iterator& other) const { @@ -338,7 +338,7 @@ public: base::copy(std::begin(tmp), std::end(tmp), data()); } -#ifdef GSL_HAS_CONTAINER_RANGES +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) template requires(std::ranges::input_range) constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}) @@ -346,7 +346,7 @@ public: { base::copy(std::ranges::begin(rg), std::ranges::end(rg), data()); } -#endif /* GSL_HAS_CONTAINER_RANGES */ +#endif /* __cpp_lib_containers_ranges >= 202202L */ constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) : base{count, alloc} @@ -433,21 +433,21 @@ public: #endif /* _MSC_VER */ }; -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) template ::value_type>> dyn_array(InputIt, InputIt, Alloc = {}) -> dyn_array::value_type, Alloc>; -#ifdef GSL_HAS_CONTAINER_RANGES +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) template >> dyn_array(std::from_range_t, InputRg&&, Alloc = {}) -> dyn_array, Alloc>; -#endif /* GSL_HAS_CONTAINER_RANGES */ +#endif /* __cpp_lib_containers_ranges >= 202202L */ -#endif /* GSL_HAS_DEDUCTION_GUIDES */ +#endif /* __cpp_deduction_guides >= 201703L */ } // namespace gsl #endif /* defined(GSL_DYN_ARRAY_H) */ diff --git a/include/gsl/pointers b/include/gsl/pointers index 131757a6..ed45c2bb 100644 --- a/include/gsl/pointers +++ b/include/gsl/pointers @@ -339,7 +339,7 @@ auto make_strict_not_null(T&& t) noexcept return strict_not_null>>{std::forward(t)}; } -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) // deduction guides to prevent the ctad-maybe-unsupported warning template @@ -347,7 +347,7 @@ not_null(T) -> not_null; template strict_not_null(T) -> strict_not_null; -#endif // GSL_HAS_DEDUCTION_GUIDES +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) } // namespace gsl diff --git a/include/gsl/span b/include/gsl/span index ebad0c61..daf66ef1 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -768,7 +768,7 @@ private: } }; -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) // Deduction Guides template @@ -788,7 +788,7 @@ template ().data())>> span(const Container&) -> span; -#endif // GSL_HAS_DEDUCTION_GUIDES +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) #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 d03ed141..01ffb4dc 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -87,26 +87,6 @@ #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 #else // ^^^ since C++20 /// before C++20 vvv diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 438a2b34..9ab514ae 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -17,15 +17,15 @@ static_assert(sizeof(gsl::dyn_array) == 2 * sizeof(void*), "gsl::dyn_array (with the default allocator) should be 16 bytes"); -#ifdef GSL_HAS_CONCEPTS +#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L) static_assert(std::input_iterator::iterator>, "gsl::dyn_array should expose a valid input_iterator"); -#endif /* GSL_HAS_CONCEPTS */ +#endif /* __cpp_lib_concepts >= 202002L */ -#ifdef GSL_HAS_RANGES +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) static_assert(std::ranges::input_range>, "gsl::dyn_array should be a valid input range"); -#endif /* GSL_HAS_RANGES */ +#endif /* __cpp_lib_ranges >= 201911L */ TEST(dyn_array_tests, default_ctor) { @@ -145,7 +145,7 @@ TEST(dyn_array_tests, use_std_algorithms) EXPECT_EQ(dodgers.end(), std::end(dodgers)); } -#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) constexpr auto default_constructed_count_dyn_array_is_constexpr() { gsl::dyn_array values(3); @@ -174,9 +174,9 @@ TEST(dyn_array_tests, constexprness) static_assert(default_constructed_count_dyn_array_is_constexpr()); static_assert(copy_assigned_dyn_array_is_constexpr()); } -#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ -#ifdef GSL_HAS_RANGES +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) TEST(dyn_array_tests, ranges) { gsl::dyn_array brewers(26); @@ -190,16 +190,16 @@ TEST(dyn_array_tests, ranges) EXPECT_EQ(std::ranges::distance(it, std::ranges::begin(brewers)), 'a' - 'c'); } -#ifdef GSL_HAS_CONTAINER_RANGES +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) std::vector twins(10, 'c'); gsl::dyn_array mets(std::from_range, 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 /* __cpp_lib_containers_ranges >= 202202L */ } -#endif /* GSL_HAS_RANGES */ +#endif /* __cpp_lib_ranges >= 201911L */ -#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) template struct ConstexprAllocator { @@ -244,7 +244,7 @@ constexpr auto operator!=(const ConstexprAllocator& lhs, { return !(lhs == rhs); } -#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ template static int AllocCounter = 0; @@ -479,20 +479,20 @@ TEST(dyn_array_tests, custom_allocator_models_allocator) auto p = traits::allocate(alloc, 1); traits::deallocate(alloc, p, 1); -#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) using constexpr_traits = std::allocator_traits>; static_assert(std::is_same::value, "allocator trait type mismatch"); -#endif /* GSL_HAS_CONSTEXPR_ALLOCATOR */ +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ } TEST(dyn_array_tests, custom_allocator) { -#ifdef GSL_HAS_CONSTEXPR_ALLOCATOR +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) 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 */ +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ Newocator::init(); { @@ -706,13 +706,13 @@ TEST(DynArrayTests, TypeConsistency) static_assert(std::is_same::difference_type, std::ptrdiff_t>::value, "Difference type mismatch"); } -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) TEST(dyn_array_tests, deduction_guides) { std::vector giants{10}; -#ifdef GSL_HAS_CONTAINER_RANGES +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) gsl::dyn_array mariners(std::from_range, giants); -#endif /* GSL_HAS_CONTAINER_RANGES */ +#endif /* __cpp_lib_containers_ranges >= 202202L */ gsl::dyn_array cardinals(std::begin(giants), std::end(giants)); } -#endif /* GSL_HAS_DEDUCTION_GUIDES */ +#endif /* __cpp_deduction_guides >= 201703L */ diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index aad88c53..081c94c2 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 -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) #ifdef __has_include #if __has_include() #include #define HAS_STRING_VIEW #endif // __has_include() #endif // __has_include -#endif // GSL_HAS_DEDUCTION_GUIDES +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) #if defined(__cplusplus) && __cplusplus >= 202002L #include #endif // __cplusplus >= 202002L @@ -1209,7 +1209,7 @@ TEST(span_test, default_constructible) TEST(span_test, std_container_ctad) { -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) // 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 // GSL_HAS_DEDUCTION_GUIDES +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) } TEST(span_test, front_back) @@ -1294,13 +1294,13 @@ TEST(span_test, conversions) { int arr[5] = {1, 2, 3, 4, 5}; -#ifdef GSL_HAS_DEDUCTION_GUIDES +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) span s = arr; span cs = s; #else // ^^^ deduction guides /// no deduction guides vvv span s = arr; span cs = s; -#endif // GSL_HAS_DEDUCTION_GUIDES +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) EXPECT_TRUE(cs.size() == s.size()); EXPECT_TRUE(cs.data() == s.data()); From f3c853c4f25f4c15d7d761260483502b0aa665ea Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Thu, 21 May 2026 14:45:20 -0600 Subject: [PATCH 24/24] misc fixes from self-review --- .github/workflows/compilers.yml | 7 +++--- include/gsl/dyn_array | 21 +---------------- include/gsl/util | 7 +++--- tests/CMakeLists.txt | 4 ---- tests/dyn_array_tests.cpp | 42 --------------------------------- 5 files changed, 7 insertions(+), 74 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index da9b25ee..6681e1a1 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -77,16 +77,16 @@ jobs: cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} extra_cmake_configure_args: '-DCMAKE_CXX_FLAGS="-isysroot \"$(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk\""' - VisualStudio: + msvc: strategy: matrix: image: [ windows-2022, windows-2025 ] build_type: [ Debug, Release ] - toolset: [ '', 'ClangCL' ] + toolset: [ 'msvc', 'ClangCL' ] cxx_version: [ 14, 17, 20, 23 ] include: # Regular MSVC builds use Ninja (from preset) - - toolset: '' + - toolset: 'msvc' generator_override: '' # ClangCL builds require Visual Studio generator - toolset: 'ClangCL' @@ -104,4 +104,3 @@ jobs: extra_cmake_configure_args: ${{ matrix.generator_override }} extra_cmake_build_args: ${{ matrix.toolset == 'ClangCL' && format('--config {0}', matrix.build_type) || '' }} extra_ctest_args: ${{ matrix.toolset == 'ClangCL' && format('-C {0}', matrix.build_type) || '' }} - diff --git a/include/gsl/dyn_array b/include/gsl/dyn_array index cd71b445..c3546f8e 100644 --- a/include/gsl/dyn_array +++ b/include/gsl/dyn_array @@ -125,16 +125,7 @@ namespace details } } - constexpr void swap(dyn_array_base& other) { - auto old_count = _count; - auto old_data = _data; - _count = other._count; - _data = other._data; - other._count = old_count; - other._data = old_data; - } - - private: + private: pointer _data; size_type _count; @@ -299,10 +290,6 @@ class dyn_array : private details::dyn_array_base using base = details::dyn_array_base; using pointer = T*; - constexpr void swap(dyn_array& other) { - base::swap(other); - } - public: using value_type = T; using reference = T&; @@ -362,12 +349,6 @@ public: : dyn_array(init.begin(), init.end(), alloc) {} - constexpr auto operator=(const dyn_array& other) -> dyn_array& { - dyn_array tmp{other, get_allocator()}; - swap(tmp); - return *this; - } - constexpr dyn_array(dyn_array&&) = delete; dyn_array& operator=(dyn_array&&) = delete; diff --git a/include/gsl/util b/include/gsl/util index 01ffb4dc..5125c35b 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -116,10 +116,9 @@ namespace details : std::true_type {}; template - struct is_fwd_iterator : std::false_type - {}; - - template + struct is_fwd_iterator : std::false_type {}; + + template struct is_fwd_iterator::iterator_category>> : std::integral_constant::iterator_category>::value> {}; } // namespace details diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b3284177..3ec624c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -76,10 +76,6 @@ include(CheckCXXCompilerFlag) # please try to keep entries ordered =) add_library(gsl_tests_config INTERFACE) if(MSVC) # MSVC or simulating MSVC - set(CMAKE_VS_GLOBALS - "EnableMicrosoftCodeAnalysis=true" - "CodeAnalysisRuleSet=AllRules.ruleset" - ) target_compile_options(gsl_tests_config INTERFACE ${GSL_CPLUSPLUS_OPT} /EHsc diff --git a/tests/dyn_array_tests.cpp b/tests/dyn_array_tests.cpp index 9ab514ae..87bff716 100644 --- a/tests/dyn_array_tests.cpp +++ b/tests/dyn_array_tests.cpp @@ -152,16 +152,6 @@ constexpr auto default_constructed_count_dyn_array_is_constexpr() return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0; } -constexpr auto copy_assigned_dyn_array_is_constexpr() -{ - gsl::dyn_array source(3, 7); - gsl::dyn_array target(2, 4); - - target = source; - - return target.size() == 3 && target[0] == 7 && target[1] == 7 && target[2] == 7; -} - TEST(dyn_array_tests, constexprness) { constexpr gsl::dyn_array marlins; @@ -172,7 +162,6 @@ TEST(dyn_array_tests, constexprness) static_assert(marlins.begin() == marlins.end()); static_assert(std::distance(marlins.begin(), marlins.end()) == 0); static_assert(default_constructed_count_dyn_array_is_constexpr()); - static_assert(copy_assigned_dyn_array_is_constexpr()); } #endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ @@ -510,23 +499,6 @@ TEST(dyn_array_tests, custom_allocator) Newocator::check(); } -TEST(dyn_array_tests, copy_assignment_deallocates_with_the_allocator_that_owns_the_storage) -{ - using allocator_type = OwnershipTrackingAllocator; - using array_type = gsl::dyn_array; - - allocator_type::reset(); - - { - array_type source(3, 's', allocator_type{1}); - array_type target(2, 't', allocator_type{2}); - - target = source; - } - - EXPECT_EQ(allocator_type::mismatched_deallocations(), 0); -} - TEST(dyn_array_tests, non_trivial_elements_are_destroyed) { LifetimeCounter::alive_count = 0; @@ -598,20 +570,6 @@ TEST(dyn_array_tests, reverse_iterator) EXPECT_EQ(*it++, 'a'); } -TEST(dyn_array_tests, copy_assignment) -{ - gsl::dyn_array rays(3, 'r'); - const gsl::dyn_array rangers{'a', 'b', 'c'}; - - auto& assigned = (rays = rangers); - - EXPECT_EQ(&assigned, &rays); - EXPECT_EQ(rays.size(), rangers.size()); - EXPECT_EQ(rays[0], 'a'); - EXPECT_EQ(rays[1], 'b'); - EXPECT_EQ(rays[2], 'c'); -} - TEST(dyn_array_tests, random_access_iterator_arithmetic) { gsl::dyn_array bluejays{'a', 'b', 'c', 'd'};