diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index e87eddad..6681e1a1 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -77,23 +77,30 @@ 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: - generator: [ 'Visual Studio 17 2022' ] image: [ windows-2022, windows-2025 ] build_type: [ Debug, Release ] - extra_args: [ '', '-T ClangCL' ] + toolset: [ 'msvc', 'ClangCL' ] cxx_version: [ 14, 17, 20, 23 ] + include: + # Regular MSVC builds use Ninja (from preset) + - toolset: 'msvc' + generator_override: '' + # 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@v6 - uses: microsoft/setup-msbuild@v3 + - uses: ilammy/msvc-dev-cmd@v1 - name: Run CMake (configure, build, test) 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.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/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" 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..dc83331a 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,178 @@ 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`. +## `` + +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. + ## `` 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..c3546f8e --- /dev/null +++ b/include/gsl/dyn_array @@ -0,0 +1,434 @@ +// -*- 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 +#include + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) +#include +#endif /* __cpp_lib_ranges >= 201911L */ + +namespace gsl +{ +namespace details +{ + template > + class dyn_array_base : public Allocator + { + 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 resize(size_type count) { + // 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) { + _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; + 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; + } + } + + 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) { + construct(current); + } + } + catch (...) { + rollback_construction(first, current); + throw; + } + } + + private: + pointer _data; + size_type _count; + + public: + 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}, + _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) { + if (!std::is_trivially_destructible::value) { + destroy_range(_data, _data + _count); + } + std::allocator_traits::deallocate(static_cast(*this), _data, _count); + } + } + }; + + template + class dyn_array_iterator + { + using size_type = std::size_t; + + public: + 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; + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) + constexpr dyn_array_iterator() = default; +#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} + { + Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0)); + Ensures(_pos <= _end_pos); + } + +#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) && __cpp_lib_ranges >= 201911L */ + + 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 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 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) const + { + 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 *this + (-diff); + } + + 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 : private details::dyn_array_base +{ + using base = details::dyn_array_base; + using pointer = T*; + +public: + 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; + + explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {} + + constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) + : base{count, alloc} + { + base::fill(data(), size(), value); + } + + 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} { + std::vector tmp(first, last); + base::resize(tmp.size()); + base::copy(std::begin(tmp), std::end(tmp), data()); + } + +#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 = {}) + : base{gsl::narrow(std::size(rg)), alloc} + { + base::copy(std::ranges::begin(rg), std::ranges::end(rg), data()); + } +#endif /* __cpp_lib_containers_ranges >= 202202L */ + + constexpr explicit dyn_array(size_type count, const Allocator& 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) + {} + + constexpr dyn_array(std::initializer_list init, const Allocator& alloc = {}) + : dyn_array(init.begin(), init.end(), alloc) + {} + + 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 base::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 base::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(); } + constexpr auto _Unchecked_begin() const -> const T* + { + 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 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(); } + constexpr auto _Unchecked_end() const -> const T* + { + return const_cast(*this)._Unchecked_end(); + } +#endif /* _MSC_VER */ +}; + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +template ::value_type>> +dyn_array(InputIt, InputIt, + Alloc = {}) -> dyn_array::value_type, Alloc>; + +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) +template >> +dyn_array(std::from_range_t, InputRg&&, + Alloc = {}) -> dyn_array, Alloc>; +#endif /* __cpp_lib_containers_ranges >= 202202L */ + +#endif /* __cpp_deduction_guides >= 201703L */ +} // namespace gsl + +#endif /* defined(GSL_DYN_ARRAY_H) */ diff --git a/include/gsl/pointers b/include/gsl/pointers index 436f09a4..ed45c2bb 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)) +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) // 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 // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) } // namespace gsl diff --git a/include/gsl/span b/include/gsl/span index cef8a774..daf66ef1 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)) +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) // Deduction Guides template @@ -787,7 +788,7 @@ template ().data())>> span(const Container&) -> span; -#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) ) +#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 7a3caed4..5125c35b 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 iterator_traits #include // for numeric_limits #include // for initializer_list #include // for is_signed, integral_constant @@ -86,6 +87,13 @@ #define GSL_DEPRECATED(msg) #endif // !defined(GSL_DEPRECATED) +#if __cplusplus >= 202002L +#define GSL_CONSTEXPR_SINCE_CPP20 constexpr +#else // ^^^ since C++20 /// before C++20 vvv +#define GSL_BEFORE_CPP20 +#define GSL_CONSTEXPR_SINCE_CPP20 +#endif // __cplusplus >= 202002L + namespace gsl { // @@ -95,6 +103,26 @@ namespace gsl // index type for all container indexes/subscripts/sizes using index = std::ptrdiff_t; +namespace details +{ + template + using void_t = void; + + template + struct is_iterator : std::false_type {}; + + 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 + // 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..87bff716 --- /dev/null +++ b/tests/dyn_array_tests.cpp @@ -0,0 +1,676 @@ +#include + +#include "deathTestCommon.h" +#include +#include +#include "gsl/dyn_array" +#include +#include +#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"); + +#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 /* __cpp_lib_concepts >= 202002L */ + +#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 /* __cpp_lib_ranges >= 201911L */ + +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)); +} + +#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); + return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0; +} + +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); + static_assert(default_constructed_count_dyn_array_is_constexpr()); +} +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) +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'); + } + +#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 /* __cpp_lib_containers_ranges >= 202202L */ +} +#endif /* __cpp_lib_ranges >= 201911L */ + +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) +template +struct ConstexprAllocator +{ + using value_type = T; + + T buf[N]{}; + std::size_t sz{}; + + 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(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 /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + +template +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 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; + 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 +{ +public: + using value_type = T; + + Newocator() = default; + + template + Newocator(const Newocator&) noexcept + {} + + static void init() + { + AllocCounter> = 0; + DeallocCounter> = 0; + } + + static void check() { EXPECT_EQ(AllocCounter>, DeallocCounter>); } + + auto allocate(std::size_t n) -> value_type* + { + AllocCounter>++; + return static_cast(::operator new(n * sizeof(value_type))); + } + + void deallocate(value_type* p, std::size_t) noexcept + { + 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); +} + +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>; + using ptr = traits::pointer; + + 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); + traits::deallocate(alloc, p, 1); + +#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 /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ +} + +TEST(dyn_array_tests, custom_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 /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + + 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, 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, 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; + 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'}; + 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'); +} + +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, 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"}; + 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::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"); +} + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) +TEST(dyn_array_tests, deduction_guides) +{ + std::vector giants{10}; +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) + gsl::dyn_array mariners(std::from_range, giants); +#endif /* __cpp_lib_containers_ranges >= 202202L */ + gsl::dyn_array cardinals(std::begin(giants), std::end(giants)); +} +#endif /* __cpp_deduction_guides >= 201703L */ diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 9b74ad44..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 -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#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 // (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#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) { -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) +#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 +#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}; -#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) span s = arr; span cs = s; -#else +#else // ^^^ deduction guides /// no deduction guides vvv span s = arr; span cs = s; -#endif +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) EXPECT_TRUE(cs.size() == s.size()); EXPECT_TRUE(cs.data() == s.data());