-
Notifications
You must be signed in to change notification settings - Fork 758
feat: implementation of gsl::dyn_array #1228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines | |
| - [`<algorithms>`](#user-content-H-algorithms) | ||
| - [`<assert>`](#user-content-H-assert) | ||
| - [`<byte>`](#user-content-H-byte) | ||
| - [`<dyn_array>`](#user-content-H-dyn_array) | ||
| - [`<gsl>`](#user-content-H-gsl) | ||
| - [`<narrow>`](#user-content-H-narrow) | ||
| - [`<pointers>`](#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`. | ||
|
|
||
| ## <a name="H-dyn_array" />`<dyn_array>` | ||
|
|
||
| # TODO (@carsonradtke) | ||
|
|
||
|
Comment on lines
+159
to
+162
|
||
| ## <a name="H-gsl" />`<gsl>` | ||
|
|
||
| This header is a convenience header that includes all other [GSL headers](#user-content-H). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,354 @@ | ||
| // -*- 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" | ||
|
Comment on lines
+21
to
+23
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
|
|
||
| #include <algorithm> | ||
| #include <iterator> | ||
| #include <memory> | ||
|
|
||
| #ifdef GSL_HAS_RANGES | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the use of |
||
| #include <ranges> | ||
| #endif /* GSL_HAS_RANGES */ | ||
carsonRadtke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| namespace gsl | ||
| { | ||
| namespace details | ||
| { | ||
|
Comment on lines
+33
to
+36
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shorten that to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the README:
|
||
| template <typename T> | ||
| 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 <typename T, typename Allocator = std::allocator<T>> | ||
| class dyn_array_base : public Allocator | ||
| { | ||
| using pointer = typename dyn_array_traits<T>::pointer; | ||
| using size_type = typename dyn_array_traits<T>::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; } | ||
|
|
||
| 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()); | ||
| } | ||
|
Comment on lines
+72
to
+81
|
||
| }; | ||
|
|
||
| template <typename T> | ||
| class dyn_array_iterator | ||
| { | ||
| using size_type = typename dyn_array_traits<T>::size_type; | ||
|
|
||
| public: | ||
| using difference_type = typename dyn_array_traits<T>::difference_type; | ||
| using value_type = typename dyn_array_traits<T>::value_type; | ||
| using pointer = typename dyn_array_traits<T>::pointer; | ||
| using reference = typename dyn_array_traits<T>::reference; | ||
| using const_reference = typename dyn_array_traits<T>::const_reference; | ||
| using iterator_category = std::random_access_iterator_tag; | ||
|
|
||
| #ifdef GSL_HAS_RANGES | ||
| constexpr dyn_array_iterator() = default; | ||
| #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<size_type>(_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 new_pos = gsl::narrow<difference_type>(_pos) + diff; | ||
| Expects(new_pos >= 0); | ||
| Expects(new_pos <= gsl::narrow<difference_type>(_end_pos)); | ||
| _pos = gsl::narrow<size_type>(new_pos); | ||
| return *this; | ||
| } | ||
|
|
||
| constexpr auto operator-=(difference_type diff) -> dyn_array_iterator& | ||
| { | ||
| auto new_pos = gsl::narrow<difference_type>(_pos) - diff; | ||
| Expects(new_pos >= 0); | ||
| Expects(new_pos <= gsl::narrow<difference_type>(_end_pos)); | ||
| _pos = gsl::narrow<size_type>(new_pos); | ||
| return *this; | ||
| } | ||
|
|
||
| constexpr auto operator+(difference_type diff) const | ||
| { | ||
| return dyn_array_iterator{_ptr, _pos + gsl::narrow<size_type>(diff), _end_pos}; | ||
| } | ||
|
|
||
| constexpr auto operator-(difference_type diff) const | ||
| { | ||
| return dyn_array_iterator{_ptr, _pos + gsl::narrow<size_type>(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<difference_type>(_pos) - gsl::narrow<difference_type>(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<dyn_array_iterator&>(*this).operator[](pos); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this triggers C26492 and should be |
||
| } | ||
|
|
||
| private: | ||
| pointer _ptr{}; | ||
| size_type _pos{}; | ||
| size_type _end_pos{}; | ||
| }; | ||
| } // namespace details | ||
|
|
||
| template <typename T, typename Allocator = std::allocator<T>> | ||
| class dyn_array : private details::dyn_array_base<T, Allocator> | ||
| { | ||
| using base = details::dyn_array_base<T, Allocator>; | ||
| using pointer = typename details::dyn_array_traits<T>::pointer; | ||
|
|
||
| public: | ||
| using value_type = typename details::dyn_array_traits<T>::value_type; | ||
| using reference = typename details::dyn_array_traits<T>::reference; | ||
| using const_reference = typename details::dyn_array_traits<T>::const_reference; | ||
| using iterator = details::dyn_array_iterator<T>; | ||
| using const_iterator = details::dyn_array_iterator<const T>; | ||
| using reverse_iterator = std::reverse_iterator<iterator>; | ||
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||
| using difference_type = typename details::dyn_array_traits<T>::difference_type; | ||
| using size_type = typename details::dyn_array_traits<T>::size_type; | ||
|
|
||
| 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} | ||
| { | ||
| std::fill(begin(), end(), value); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UB. The base class called the allocator but did not construct any object. So you cannot call gemini says someone should call Maybe a test with a class can be added for test coverage? |
||
| } | ||
|
|
||
| GSL_TYPE_IS_ITERATOR(InputIt) | ||
| constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) | ||
| : base{gsl::narrow<size_type>(std::distance(first, last)), alloc} | ||
| { | ||
| std::copy(first, last, begin()); | ||
| } | ||
|
|
||
| #ifdef GSL_HAS_CONTAINER_RANGES | ||
| template <typename InputRg> | ||
| requires(std::ranges::input_range<InputRg>) | ||
| constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}) | ||
| : base{gsl::narrow<size_type>(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<T> 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 base::_impl.count(); } | ||
|
|
||
| constexpr auto empty() const { return size() == 0; } | ||
|
|
||
| constexpr auto max_size() const { return static_cast<size_type>(-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<dyn_array&>(*this)[pos]; | ||
| } | ||
|
|
||
| constexpr auto data() { return base::_impl.data(); } | ||
| constexpr auto data() const -> const T* { return const_cast<dyn_array&>(*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 pointer | ||
| { | ||
| return const_cast<dyn_array&>(*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 pointer | ||
| { | ||
| return const_cast<dyn_array&>(*this)._Unchecked_end(); | ||
| } | ||
| #endif /* MSC_VER */ | ||
carsonRadtke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| #ifdef GSL_HAS_DEDUCTION_GUIDES | ||
|
|
||
| template <class InputIt, | ||
| class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>> | ||
| dyn_array(InputIt, InputIt, | ||
| Alloc = {}) -> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>; | ||
|
|
||
| #ifdef GSL_HAS_CONTAINER_RANGES | ||
| template <std::ranges::input_range InputRg, | ||
| class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>> | ||
| dyn_array(std::from_range_t, InputRg&&, | ||
| Alloc = {}) -> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>; | ||
| #endif /* GSL_HAS_RANGES */ | ||
|
|
||
| #endif /* GSL_HAS_DEDUCTION_GUIDES */ | ||
| } // namespace gsl | ||
|
|
||
| #endif /* defined(GSL_DYN_ARRAY_H) */ | ||
Uh oh!
There was an error while loading. Please reload this page.