Skip to content

Commit bb8b58e

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 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) 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
1 parent 756c91a commit bb8b58e

File tree

9 files changed

+697
-13
lines changed

9 files changed

+697
-13
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: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
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 const_reference = typename dyn_array_traits<T>::const_reference;
95+
using iterator_category = std::random_access_iterator_tag;
96+
97+
#ifdef GSL_HAS_RANGES
98+
constexpr dyn_array_iterator() : dyn_array_iterator(nullptr, 0, 0) {}
99+
#endif /* GSL_HAS_RANGES */
100+
101+
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
102+
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
103+
{
104+
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
105+
Ensures(_pos <= _end_pos);
106+
}
107+
108+
constexpr auto operator==(const dyn_array_iterator& other) const
109+
{
110+
Expects(_ptr == other._ptr);
111+
Expects(_end_pos == other._end_pos);
112+
return _pos == other._pos;
113+
}
114+
115+
constexpr auto operator!=(const dyn_array_iterator& other) const
116+
{
117+
return !(*this == other);
118+
}
119+
120+
constexpr auto operator*() const -> reference
121+
{
122+
Expects(_ptr != nullptr);
123+
Expects(_pos < _end_pos);
124+
return _ptr[_pos];
125+
}
126+
127+
constexpr auto operator++() -> dyn_array_iterator&
128+
{
129+
Expects(_pos < _end_pos);
130+
_pos++;
131+
return *this;
132+
}
133+
134+
constexpr auto operator++(int)
135+
{
136+
auto rv = *this;
137+
++(*this);
138+
return rv;
139+
}
140+
141+
constexpr auto operator--() -> dyn_array_iterator&
142+
{
143+
Expects(_pos > 0);
144+
_pos--;
145+
return *this;
146+
}
147+
148+
constexpr auto operator--(int)
149+
{
150+
auto rv = *this;
151+
--(*this);
152+
return rv;
153+
}
154+
155+
constexpr auto operator+=(difference_type diff) -> dyn_array_iterator&
156+
{
157+
auto pos = gsl::narrow<difference_type>(_pos);
158+
Expects(pos + diff <= gsl::narrow<difference_type>(_end_pos));
159+
_pos = gsl::narrow<size_type>(pos + diff);
160+
return *this;
161+
}
162+
163+
constexpr auto operator-=(difference_type diff) -> dyn_array_iterator&
164+
{
165+
auto pos = gsl::narrow<difference_type>(_pos);
166+
Expects(pos >= diff);
167+
_pos = gsl::narrow<size_type>(pos - diff);
168+
return *this;
169+
}
170+
171+
constexpr auto operator+(difference_type diff) const
172+
{
173+
return dyn_array_iterator{_ptr, gsl::narrow<difference_type>(_pos) + diff, _end_pos};
174+
}
175+
176+
constexpr auto operator-(difference_type diff) const
177+
{
178+
return dyn_array_iterator{_ptr, gsl::narrow<difference_type>(_pos) - diff, _end_pos};
179+
}
180+
181+
constexpr auto operator-(const dyn_array_iterator& other) const
182+
{
183+
Expects(_ptr == other._ptr);
184+
Expects(_end_pos == other._end_pos);
185+
return gsl::narrow<difference_type>(_pos) - gsl::narrow<difference_type>(other._pos);
186+
}
187+
188+
constexpr auto operator[](size_type pos) -> reference
189+
{
190+
Expects(_pos + pos < _end_pos);
191+
return _ptr[_pos + pos];
192+
}
193+
194+
constexpr auto operator[](size_type pos) const -> const_reference
195+
{
196+
return const_cast<dyn_array_iterator&>(*this).operator[](pos);
197+
}
198+
199+
private:
200+
pointer _ptr;
201+
size_type _pos;
202+
size_type _end_pos;
203+
};
204+
} // namespace details
205+
206+
template <typename T, typename Allocator = std::allocator<T>>
207+
class dyn_array : public details::dyn_array_base<T, Allocator>
208+
{
209+
using base = details::dyn_array_base<T, Allocator>;
210+
using pointer = typename details::dyn_array_traits<T>::pointer;
211+
212+
public:
213+
using value_type = typename details::dyn_array_traits<T>::value_type;
214+
using reference = typename details::dyn_array_traits<T>::reference;
215+
using const_reference = typename details::dyn_array_traits<T>::const_reference;
216+
using iterator = details::dyn_array_iterator<T>;
217+
using const_iterator = details::dyn_array_iterator<const T>;
218+
using reverse_iterator = std::reverse_iterator<iterator>;
219+
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
220+
using difference_type = typename details::dyn_array_traits<T>::difference_type;
221+
using size_type = typename details::dyn_array_traits<T>::size_type;
222+
223+
using allocator_type = Allocator;
224+
225+
explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {}
226+
227+
explicit constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {})
228+
: base{count, alloc}
229+
{
230+
std::fill(begin(), end(), value);
231+
}
232+
233+
GSL_TYPE_IS_ITERATOR(InputIt)
234+
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {})
235+
: base{gsl::narrow<size_type>(std::distance(first, last)), alloc}
236+
{
237+
std::copy(first, last, begin());
238+
}
239+
240+
#ifdef GSL_HAS_CONTAINER_RANGES
241+
template <typename InputRg>
242+
requires(std::ranges::input_range<InputRg>)
243+
constexpr dyn_array(std::from_range_t, InputRg && rg, const Allocator& alloc = {})
244+
: base{gsl::narrow<size_type>(std::size(rg)), alloc}
245+
{
246+
std::ranges::copy(rg, std::ranges::begin(*this));
247+
}
248+
#endif /* GSL_HAS_RANGES */
249+
250+
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {})
251+
: dyn_array{count, T{}, alloc}
252+
{}
253+
254+
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {})
255+
: dyn_array(other.begin(), other.end(), alloc)
256+
{}
257+
258+
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {})
259+
: dyn_array(init.begin(), init.end(), alloc)
260+
{}
261+
262+
constexpr auto operator=(const dyn_array& other) -> dyn_array& { return dyn_array{other}; }
263+
264+
constexpr dyn_array(dyn_array&&) = delete;
265+
dyn_array& operator=(dyn_array&&) = delete;
266+
267+
constexpr auto operator==(const dyn_array& other) const { return this == &other; }
268+
269+
constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); }
270+
271+
constexpr auto size() const { return impl().count(); }
272+
273+
constexpr auto empty() const { return size() == 0; }
274+
275+
constexpr auto max_size() const { return static_cast<size_type>(-1); }
276+
277+
constexpr auto get_allocator() -> Allocator& { return *this; }
278+
279+
constexpr auto operator[](size_type pos) -> reference
280+
{
281+
Expects(pos < size());
282+
return data()[pos];
283+
}
284+
285+
constexpr auto operator[](size_type pos) const -> const_reference
286+
{
287+
return const_cast<dyn_array&>(*this)[pos];
288+
}
289+
290+
constexpr auto data() { return impl().data(); }
291+
constexpr auto data() const -> const T* { return const_cast<dyn_array&>(*this).data(); }
292+
293+
constexpr auto begin() { return iterator{data(), 0, size()}; }
294+
constexpr auto begin() const { return const_iterator{data(), 0, size()}; }
295+
296+
constexpr auto rbegin() { return reverse_iterator{end()}; }
297+
constexpr auto rbegin() const { return const_reverse_iterator{end()};}
298+
299+
#ifdef _MSC_VER
300+
constexpr auto _Unchecked_begin() { return data(); }
301+
constexpr auto _Unchecked_begin() const -> const pointer { return const_cast<dyn_array&>(*this).end(); }
302+
#endif /* _MSC_VER */
303+
304+
constexpr auto end() { return iterator{data(), size(), size()}; }
305+
constexpr auto end() const { return const_iterator{data(), size(), size()}; }
306+
307+
constexpr auto rend() { return reverse_iterator{begin()}; }
308+
constexpr auto rend() const { return const_reverse_iterator{begin()};}
309+
310+
#ifdef _MSC_VER
311+
constexpr auto _Unchecked_end() { return data() + size(); }
312+
constexpr auto _Unchecked_end() const -> const pointer { return const_cast<dyn_array&>(*this).end(); }
313+
#endif /* MSC_VER */
314+
315+
private:
316+
constexpr auto impl() const { return base::impl(); }
317+
};
318+
319+
#ifdef GSL_HAS_DEDUCTION_GUIDES
320+
321+
template <class InputIt,
322+
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
323+
dyn_array(InputIt, InputIt, Alloc={}) -> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>;
324+
325+
#ifdef GSL_HAS_CONTAINER_RANGES
326+
template <std::ranges::input_range InputRg, class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>>
327+
dyn_array(std::from_range_t, InputRg&&, Alloc={}) -> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>;
328+
#endif /* GSL_HAS_RANGES */
329+
330+
#endif /* GSL_HAS_DEDUCTION_GUIDES */
331+
} // namespace gsl
332+
333+
#endif /* defined(GSL_DYN_ARRAY_H) */

include/gsl/pointers

Lines changed: 3 additions & 2 deletions
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.
@@ -338,15 +339,15 @@ auto make_strict_not_null(T&& t) noexcept
338339
return strict_not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
339340
}
340341

341-
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
342+
#ifdef GSL_HAS_DEDUCTION_GUIDES
342343

343344
// deduction guides to prevent the ctad-maybe-unsupported warning
344345
template <class T>
345346
not_null(T) -> not_null<T>;
346347
template <class T>
347348
strict_not_null(T) -> strict_not_null<T>;
348349

349-
#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
350+
#endif // GSL_HAS_DEDUCTION_GUIDES
350351

351352
} // namespace gsl
352353

0 commit comments

Comments
 (0)