Skip to content

Commit 8acdd5a

Browse files
committed
feat: implementation of gsl::dyn_array
Implement gsl::dyn_array<T, Allocator> as specified by the CppCoreGuidlines here: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/dyn_array.md 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 What needs to be done: - [ ] Tests/Support for operations on const gsl::dyn_array - [ ] Tests/Support for MSVC's unchecked iterators - [ ] Tests/Support for construction of gsl::dyn_array from std::initializer_list - [ ] Tests/Support for reverse iterators - [ ] Tests for const iterators - [ ] Deduction guides for gsl::dyn_array - [ ] Sanity-check static_assertions to make sure gsl::dyn_array is (mostly) a proper allocator-aware container - [ ] Sanity-check static_assertions to make sure gsl::dyn_array::iterator is a proper random-access iterator. - [ ] Run dyn_array tests under ASAN - [ ] Ensure support for clang, gcc, MSVC
1 parent 756c91a commit 8acdd5a

File tree

5 files changed

+594
-2
lines changed

5 files changed

+594
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ span_p | &#x26
4141
[cu32zstring](docs/headers.md#user-content-H-zstring) | &#x2611; | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t`
4242
[**2. Owners**][cg-owners] | |
4343
stack_array | &#x2610; | A stack-allocated array
44-
dyn_array | &#x2610; | A heap-allocated array
44+
dyn_array | &#x2611; | A heap-allocated array
4545
[**3. Assertions**][cg-assertions] | |
4646
[Expects](docs/headers.md#user-content-H-assert-expects) | &#x2611; | A precondition assertion; on failure it terminates
4747
[Ensures](docs/headers.md#user-content-H-assert-ensures) | &#x2611; | A postcondition assertion; on failure it terminates

docs/headers.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines
99
- [`<algorithms>`](#user-content-H-algorithms)
1010
- [`<assert>`](#user-content-H-assert)
1111
- [`<byte>`](#user-content-H-byte)
12+
- [`<dyn_array>`](#user-content-H-dyn_array)
1213
- [`<gsl>`](#user-content-H-gsl)
1314
- [`<narrow>`](#user-content-H-narrow)
1415
- [`<pointers>`](#user-content-H-pointers)
@@ -155,6 +156,10 @@ constexpr byte to_byte() noexcept;
155156

156157
Convert the given value `I` to a `byte`. The template requires `I` to be in the valid range 0..255 for a `gsl::byte`.
157158

159+
## <a name="H-dyn_array" />`<dyn_array>`
160+
161+
# TODO (@carsonradtke)
162+
158163
## <a name="H-gsl" />`<gsl>`
159164

160165
This header is a convenience header that includes all other [GSL headers](#user-content-H).

include/gsl/dyn_array

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// -*- C++ -*-
2+
///////////////////////////////////////////////////////////////////////////////
3+
//
4+
// Copyright (c) 2026 Microsoft Corporation. All rights reserved.
5+
//
6+
// This code is licensed under the MIT License (MIT).
7+
//
8+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14+
// THE SOFTWARE.
15+
//
16+
///////////////////////////////////////////////////////////////////////////////
17+
18+
#ifndef GSL_DYN_ARRAY_H
19+
#define GSL_DYN_ARRAY_H
20+
21+
#include "./assert"
22+
#include "./narrow"
23+
#include "./util"
24+
25+
#include <iterator>
26+
#include <memory>
27+
28+
#ifdef GSL_HAS_RANGES
29+
#include <ranges>
30+
#endif /* GSL_HAS_RANGES */
31+
32+
namespace gsl
33+
{
34+
namespace details
35+
{
36+
template <typename T>
37+
struct dyn_array_traits
38+
{
39+
using value_type = T;
40+
using pointer = T*;
41+
using reference = T&;
42+
using const_reference = const T&;
43+
using difference_type = std::ptrdiff_t;
44+
using size_type = decltype(sizeof(0));
45+
};
46+
47+
template <typename T, typename Allocator = std::allocator<T>>
48+
class dyn_array_base : public Allocator
49+
{
50+
using pointer = typename dyn_array_traits<T>::pointer;
51+
using size_type = typename dyn_array_traits<T>::size_type;
52+
53+
const class dyn_array_impl
54+
{
55+
public:
56+
constexpr dyn_array_impl(pointer data, size_type count) : _data{data}, _count{count}
57+
{
58+
Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr));
59+
}
60+
61+
constexpr auto data() const { return _data; }
62+
63+
constexpr auto count() const { return _count; }
64+
65+
private:
66+
pointer _data;
67+
size_type _count;
68+
} _impl;
69+
70+
public:
71+
constexpr dyn_array_base(const Allocator& alloc) : Allocator{alloc}, _impl{nullptr, 0} {}
72+
constexpr dyn_array_base(size_type count, const Allocator& alloc)
73+
: Allocator{alloc}, _impl{count == 0 ? nullptr : Allocator::allocate(count), count}
74+
{}
75+
76+
GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base()
77+
{
78+
if (impl().data()) Allocator::deallocate(impl().data(), impl().count());
79+
}
80+
81+
constexpr auto impl() const -> const dyn_array_impl& { return _impl; }
82+
};
83+
84+
template <typename T>
85+
class dyn_array_iterator
86+
{
87+
using size_type = typename dyn_array_traits<T>::size_type;
88+
89+
public:
90+
using difference_type = typename dyn_array_traits<T>::difference_type;
91+
using value_type = typename dyn_array_traits<T>::value_type;
92+
using pointer = typename dyn_array_traits<T>::pointer;
93+
using reference = typename dyn_array_traits<T>::reference;
94+
using iterator_category = std::random_access_iterator_tag;
95+
96+
#ifdef GSL_HAS_RANGES
97+
constexpr dyn_array_iterator() : dyn_array_iterator(nullptr, 0, 0) {}
98+
#endif /* GSL_HAS_RANGES */
99+
100+
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
101+
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
102+
{
103+
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
104+
Ensures(_pos <= _end_pos);
105+
}
106+
107+
constexpr auto operator==(const dyn_array_iterator& other) const
108+
{
109+
Expects(_ptr == other._ptr);
110+
Expects(_end_pos == other._end_pos);
111+
return _pos == other._pos;
112+
}
113+
114+
constexpr auto operator!=(const dyn_array_iterator& other) const
115+
{
116+
return !(*this == other);
117+
}
118+
119+
constexpr auto operator*() const -> reference
120+
{
121+
Expects(_ptr != nullptr);
122+
Expects(_pos < _end_pos);
123+
return _ptr[_pos];
124+
}
125+
126+
constexpr auto operator++() -> dyn_array_iterator&
127+
{
128+
Expects(_pos < _end_pos);
129+
_pos++;
130+
return *this;
131+
}
132+
133+
constexpr auto operator++(int) -> dyn_array_iterator&
134+
{
135+
auto& rv = *this;
136+
++(*this);
137+
return rv;
138+
}
139+
140+
constexpr auto operator--() -> dyn_array_iterator&
141+
{
142+
Expects(_pos > 0);
143+
_pos--;
144+
return *this;
145+
}
146+
147+
constexpr auto operator--(int) -> dyn_array_iterator&
148+
{
149+
auto& rv = *this;
150+
--(*this);
151+
return rv;
152+
}
153+
154+
constexpr auto operator+=(difference_type diff) -> dyn_array_iterator&
155+
{
156+
auto pos = gsl::narrow<difference_type>(_pos);
157+
Expects(pos + diff <= gsl::narrow<difference_type>(_end_pos));
158+
_pos = gsl::narrow<size_type>(pos + diff);
159+
return *this;
160+
}
161+
162+
constexpr auto operator-=(difference_type diff) -> dyn_array_iterator&
163+
{
164+
auto pos = gsl::narrow<difference_type>(_pos);
165+
Expects(pos >= diff);
166+
_pos = gsl::narrow<size_type>(pos - diff);
167+
return *this;
168+
}
169+
170+
constexpr auto operator+(difference_type diff) const
171+
{
172+
return dyn_array_iterator{_ptr, gsl::narrow<difference_type>(_pos) + diff, _end_pos};
173+
}
174+
175+
constexpr auto operator-(difference_type diff) const
176+
{
177+
return dyn_array_iterator{_ptr, gsl::narrow<difference_type>(_pos) - diff, _end_pos};
178+
}
179+
180+
constexpr auto operator-(const dyn_array_iterator& other) const
181+
{
182+
Expects(_ptr == other._ptr);
183+
Expects(_end_pos == other._end_pos);
184+
return gsl::narrow<difference_type>(_pos) - gsl::narrow<difference_type>(other._pos);
185+
}
186+
187+
constexpr auto operator[](size_type pos) const -> reference
188+
{
189+
Expects(_pos + pos < _end_pos);
190+
return _ptr[_pos + pos];
191+
}
192+
193+
private:
194+
pointer _ptr;
195+
size_type _pos;
196+
size_type _end_pos;
197+
};
198+
} // namespace details
199+
200+
template <typename T, typename Allocator = std::allocator<T>>
201+
class dyn_array : public details::dyn_array_base<T, Allocator>
202+
{
203+
using base = details::dyn_array_base<T, Allocator>;
204+
205+
public:
206+
using value_type = typename details::dyn_array_traits<T>::value_type;
207+
using reference = typename details::dyn_array_traits<T>::reference;
208+
using const_reference = typename details::dyn_array_traits<T>::const_reference;
209+
using iterator = details::dyn_array_iterator<T>;
210+
using const_iterator = details::dyn_array_iterator<const T>;
211+
using reverse_iterator = std::reverse_iterator<iterator>;
212+
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
213+
using difference_type = typename details::dyn_array_traits<T>::difference_type;
214+
using size_type = typename details::dyn_array_traits<T>::size_type;
215+
216+
using allocator_type = Allocator;
217+
218+
explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {}
219+
220+
explicit constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {})
221+
: base{count, alloc}
222+
{
223+
std::fill(begin(), end(), value);
224+
}
225+
226+
GSL_TYPE_IS_ITERATOR(InputIt)
227+
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {})
228+
: base{gsl::narrow<size_type>(std::distance(first, last)), alloc}
229+
{
230+
std::copy(first, last, begin());
231+
}
232+
233+
#ifdef GSL_HAS_RANGES
234+
template <typename InputRg>
235+
requires(std::ranges::input_range<InputRg>)
236+
constexpr dyn_array(InputRg rg, const Allocator& alloc = {})
237+
: base{gsl::narrow<size_type>(std::size(rg)), alloc}
238+
{
239+
std::ranges::copy(rg, std::ranges::begin(*this));
240+
}
241+
#endif /* GSL_HAS_RANGES */
242+
243+
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {})
244+
: dyn_array{count, T{}, alloc}
245+
{}
246+
247+
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {})
248+
: dyn_array(other.begin(), other.end(), alloc)
249+
{}
250+
251+
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {})
252+
: dyn_array(init.begin(), init.end(), alloc)
253+
{}
254+
255+
constexpr auto operator=(const dyn_array& other) -> dyn_array& { return dyn_array{other}; }
256+
257+
constexpr dyn_array(dyn_array&&) = delete;
258+
dyn_array& operator=(dyn_array&&) = delete;
259+
260+
constexpr auto operator==(const dyn_array& other) const { return this == &other; }
261+
262+
constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); }
263+
264+
constexpr auto size() const { return impl().count(); }
265+
266+
constexpr auto empty() const { return size() == 0; }
267+
268+
constexpr auto max_size() const { return static_cast<size_type>(-1); }
269+
270+
constexpr auto get_allocator() -> Allocator& { return *this; }
271+
272+
constexpr auto operator[](size_type pos) -> reference
273+
{
274+
Expects(pos < size());
275+
return data()[pos];
276+
}
277+
278+
constexpr auto operator[](size_type pos) const -> const reference
279+
{
280+
return const_cast<dyn_array&>(*this)[pos];
281+
}
282+
283+
constexpr auto data() { return impl().data(); }
284+
constexpr auto data() const -> const T* { return const_cast<dyn_array&>(*this).data(); }
285+
286+
constexpr auto begin() { return iterator{data(), 0, size()}; }
287+
constexpr auto begin() const { return const_iterator{data(), 0, size()}; }
288+
289+
constexpr auto end() { return iterator{data(), size(), size()}; }
290+
constexpr auto end() const { return const_iterator{data(), size(), size()}; }
291+
292+
private:
293+
constexpr auto impl() const { return base::impl(); }
294+
};
295+
} // namespace gsl
296+
297+
#endif /* defined(GSL_DYN_ARRAY_H) */

include/gsl/util

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// -*- C++ -*-
12
///////////////////////////////////////////////////////////////////////////////
23
//
34
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
@@ -19,8 +20,8 @@
1920

2021
#include "./assert" // for Expects
2122

22-
#include <array>
2323
#include <cstddef> // for ptrdiff_t, size_t
24+
#include <iterator> // for is_iterator, iterator_traits
2425
#include <limits> // for numeric_limits
2526
#include <initializer_list> // for initializer_list
2627
#include <type_traits> // for is_signed, integral_constant
@@ -86,6 +87,27 @@
8687
#define GSL_DEPRECATED(msg)
8788
#endif // !defined(GSL_DEPRECATED)
8889

90+
#if defined(__cpp_lib_ranges)
91+
#define GSL_HAS_RANGES
92+
#endif // defined(__cpp_lib_ranges)
93+
94+
#if defined(__cpp_lib_concepts)
95+
#define GSL_HAS_CONCEPTS
96+
#endif // define(__cpp_lib_concepts)
97+
98+
#if __cplusplus >= 202002L
99+
#define GSL_CONSTEXPR_ALLOCATOR
100+
#define GSL_CONSTEXPR_SINCE_CPP20 constexpr
101+
#define GSL_TYPE_IS_ITERATOR(Ty) \
102+
template <typename Ty> \
103+
requires(std::input_iterator<Ty>)
104+
#else // ^^^ since C++20 /// before C++20 vvv
105+
#define GSL_UNTIL_CPP20
106+
#define GSL_CONSTEXPR_SINCE_CPP20
107+
#define GSL_TYPE_IS_ITERATOR(Ty) \
108+
template <typename Ty, std::enable_if_t<details::is_iterator<Ty>::value, bool> = true>
109+
#endif // __cplusplus >= 202002L
110+
89111
namespace gsl
90112
{
91113
//
@@ -95,6 +117,22 @@ namespace gsl
95117
// index type for all container indexes/subscripts/sizes
96118
using index = std::ptrdiff_t;
97119

120+
#ifdef GSL_UNTIL_CPP20
121+
namespace details
122+
{
123+
template <typename ...>
124+
using void_t = void;
125+
126+
template <typename T, typename = void>
127+
struct is_iterator : std::false_type
128+
{};
129+
130+
template <typename T>
131+
struct is_iterator<T, void_t<typename std::iterator_traits<T>::value_type>>
132+
: std::true_type {};
133+
} // namespace details
134+
#endif /* GSL_UNTIL_CPP20 */
135+
98136
// final_action allows you to ensure something gets run at the end of a scope
99137
template <class F>
100138
class final_action

0 commit comments

Comments
 (0)