• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DNKpp / Simple-Utility / 6497034454

12 Oct 2023 02:27PM UTC coverage: 86.872% (-12.8%) from 99.712%
6497034454

Pull #71

github

web-flow
Merge 967744a15 into e4166ac5d
Pull Request #71: graph namespace

281 of 281 new or added lines in 17 files covered. (100.0%)

622 of 716 relevant lines covered (86.87%)

154.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.74
/include/Simple-Utility/nullables/adapter.hpp
1
//          Copyright Dominic Koepke 2019 - 2023.
2
// Distributed under the Boost Software License, Version 1.0.
3
//    (See accompanying file LICENSE_1_0.txt or copy at
4
//          https://www.boost.org/LICENSE_1_0.txt)
5

6
#ifndef SL_UTILITY_NULLABLES_ADAPTER_HPP
7
#define SL_UTILITY_NULLABLES_ADAPTER_HPP
8

9
#pragma once
10

11
#include "Simple-Utility/Config.hpp"
12
#include "Simple-Utility/concepts/stl_extensions.hpp"
13
#include "Simple-Utility/nullables/base.hpp"
14

15
#include <cassert>
16
#include <ranges>
17

18
namespace sl::nullables
19
{
20
        namespace detail
21
        {
22
                template <class TAdapted>
23
                        requires concepts::dereferencable<TAdapted>
24
                                        && requires { { *std::declval<TAdapted>() } -> concepts::not_same_as<void>; }
25
                [[nodiscard]]
26
                constexpr decltype(auto) unwrap_adapted(TAdapted&& adapted)
16✔
27
                {
28
                        return *std::forward<TAdapted>(adapted);
16✔
29
                }
30

31
                struct unwrap_adapted_fn
32
                {
33
                        template <class TAdapted>
34
                                requires requires { unwrap_adapted(std::declval<TAdapted>()); }
35
                        [[nodiscard]]
36
                        constexpr decltype(auto) operator()(
18✔
37
                                TAdapted&& adapted
38
                        ) const
39
                                noexcept(noexcept(unwrap_adapted(std::forward<TAdapted>(adapted))))
40
                        {
41
                                return unwrap_adapted(std::forward<TAdapted>(adapted));
18✔
42
                        }
43
                };
44
        }
45

46
        inline constexpr detail::unwrap_adapted_fn unwrap_adapted{};
47
}
48

49
namespace sl::nullables
50
{
51
        /**
52
         * \defgroup GROUP_NULLABLES_ADAPTER adapter
53
         * \ingroup GROUP_NULLABLES
54
         *  @{
55
         */
56

57
        /**
58
         * \brief Dedicated null type for \ref sl::nullables::adapter "adapters".
59
         * \relates sl::nullables::adapter
60
         */
61
        struct adapter_null_t
62
        {
63
        };
64

65
        /**
66
         * \brief Dedicated null object for \ref sl::nullables::adapter "adapters".
67
         * \relates sl::nullables::adapter
68
         */
69
        inline constexpr adapter_null_t adapter_null{};
70

71
        /**
72
         * \brief Tag type for \ref sl::nullables::adapter "adapters", which can be used to disambiguate the construction
73
         * with just a null-object.
74
         * \relates sl::nullables::adapter
75
         */
76
        struct in_place_null_t
77
        {
78
        };
79

80
        /**
81
         * \brief Tag object for \ref sl::nullables::adapter "adapters", which can be used to disambiguate the construction
82
         * with just a null-object.
83
         * \relates sl::nullables::adapter
84
         */
85
        inline constexpr in_place_null_t in_place_null{};
86

87
        template <class T>
88
        using adapted_value_t = std::remove_cvref_t<decltype(unwrap_adapted(std::declval<T>()))>;
89

90
        /**
91
         * \brief Determines whether the given adapted and null type satisfy the requirements to be used within a \ref sl::nullables::adapter "adapter".
92
         * \tparam TAdapted The adapted type.
93
         * \tparam TNull The null type.
94
         */
95
        template <class TAdapted, class TNull>
96
        concept adaptable_with = std::movable<TNull>
97
                                                        && std::movable<TAdapted>
98
                                                        && concepts::weakly_equality_comparable_with<TAdapted, TNull>
99
                                                        && requires
100
                                                        {
101
                                                                typename adapted_value_t<TAdapted>;
102
                                                                { unwrap_adapted(std::declval<TAdapted>()) } -> std::convertible_to<adapted_value_t<TAdapted>>;
103
                                                        };
104

105
        template <class TNull, adaptable_with<TNull> TAdapted>
106
                requires std::same_as<TNull, std::remove_cvref_t<TNull>>
107
                                && std::same_as<TAdapted, std::remove_cvref_t<TAdapted>>
108
        class adapter;
109
}
110

111
namespace sl::nullables::detail
112
{
113
        template <std::ranges::borrowed_range TRange>
114
        [[nodiscard]]
115
        constexpr adapter<std::ranges::sentinel_t<TRange>, std::ranges::iterator_t<TRange>> to_nullables_adapter(TRange&& range)
2✔
116
        {
117
                return {std::ranges::end(range), std::ranges::begin(range)};
2✔
118
        }
119

120
        struct to_nullables_adapter_fn
121
        {
122
                template <class TArg>
123
                        requires requires { adapter{to_nullables_adapter(std::declval<TArg>())}; }
124
                [[nodiscard]]
125
                constexpr auto operator()(TArg&& arg) const noexcept(noexcept(to_nullables_adapter(std::forward<TArg>(arg))))
6✔
126
                {
127
                        return to_nullables_adapter(std::forward<TArg>(arg));
6✔
128
                }
129
        };
130
}
131

132
namespace sl::nullables
133
{
134
        /**
135
         * \brief Converts the given argument to a \ref sl::nullables::adapter "adapter" object.
136
         * \ingroup GROUP_NULLABLES_CUSTOMIZATION_POINTS
137
         * \relates sl::nullables::adapter
138
         * \details This is a customization point, which users may hook to add convenient conversions from there
139
         * types to adapters.
140
         */
141
        inline constexpr detail::to_nullables_adapter_fn to_nullables_adapter{};
142

143
        namespace detail
144
        {
145
                template <class T>
146
                concept convertible_to_adapter = requires
147
                {
148
                        adapter{nullables::to_nullables_adapter(std::declval<T>())};
149
                };
150
        }
151

152
        /**
153
         * \brief A adapter class, mimic the behaviour of nullable types.
154
         * \tparam TNull The null type.
155
         * \tparam TAdapted The adapted type.
156
         * \details This class aims to provide a layer of abstraction for types, which logically have a null-state but no dedicated null-object to compare with.
157
         * For example an arbitrary iterator can be compared to its end iterator, but that end iterator usually must be retrieved from the container (unless
158
         * end iterator is a dedicated sentinel type). adapter aims to fill the gap and therefore accepts a state object and a null object. The adapter itself
159
         * can be then compared to its dedicated ``adapter_null`` object and thus satisfying at least the ``input_nullable`` concept. If the state object
160
         * is then initializable and constructible via the given null-object, the ``nullable`` concept is also satisfied.
161
         */
162
        template <class TNull, adaptable_with<TNull> TAdapted>
163
                requires std::same_as<TNull, std::remove_cvref_t<TNull>>
164
                                && std::same_as<TAdapted, std::remove_cvref_t<TAdapted>>
165
        class adapter
166
        {
167
        public:
168
                using adapted_type = TAdapted;
169
                using value_type = adapted_value_t<TAdapted>;
170
                using null_type = TNull;
171

172
                /**
173
                 * \brief Default destructor.
174
                 */
175
                constexpr ~adapter() noexcept = default;
176

177
                /**
178
                 * \brief Default copy constructor.
179
                 * \param other The other adapter.
180
                 */
181
                [[nodiscard]]
182
                constexpr adapter(
183
                        const adapter& other
184
                ) noexcept(std::is_nothrow_copy_constructible_v<TNull>
185
                                        && std::is_nothrow_copy_constructible_v<TAdapted>)
186
                = default;
187

188
                /**
189
                 * \brief Default copy assignment operator.
190
                 * \param other The other adapter.
191
                 */
192
                constexpr adapter& operator =(
193
                        const adapter& other
194
                ) noexcept(std::is_nothrow_copy_assignable_v<TNull>
195
                                        && std::is_nothrow_copy_assignable_v<TAdapted>)
196
                = default;
197

198
                /**
199
                 * \brief Default move constructor.
200
                 * \param other The other adapter.
201
                 */
202
                [[nodiscard]]
203
                constexpr adapter(
204
                        adapter&& other
205
                ) noexcept(std::is_nothrow_move_constructible_v<TNull>
206
                                        && std::is_nothrow_move_constructible_v<TAdapted>)
207
                = default;
208

209
                /**
210
                 * \brief Default move assignment operator.
211
                 * \param other The other adapter.
212
                 */
213
                constexpr adapter& operator =(
214
                        adapter&& other
215
                ) noexcept(std::is_nothrow_move_assignable_v<TNull>
216
                                        && std::is_nothrow_move_assignable_v<TAdapted>)
217
                = default;
218

219
                /**
220
                 * \brief Constructs the null object with the given argument and then constructs the adapted object with the null object.
221
                 * \tparam TNullArg The constructor argument type for the null object.
222
                 * \param nullArg  The constructor argument for the null object.
223
                 * \attention The behaviour is undefined if after the construction adapted does not compare equal to the null object.
224
                 */
225
                template <concepts::initializes<null_type> TNullArg>
226
                        requires std::constructible_from<adapted_type, const null_type&>
227
                [[nodiscard]]
228
                explicit
229
                constexpr adapter(
8✔
230
                        [[maybe_unused]] in_place_null_t,
231
                        TNullArg&& nullArg
232
                )
233
                        noexcept(std::is_nothrow_constructible_v<null_type, TNullArg>
234
                                        && std::is_nothrow_constructible_v<adapted_type, const null_type&>)
235
                        : m_Null{std::forward<TNullArg>(nullArg)},
8✔
236
                        m_Adapted{m_Null}
8✔
237
                {
238
                        assert(*this == adapter_null && "Default constructed adapted_type must comapare equally to the provided null object.");
239
                }
8✔
240

241
                /**
242
                 * \brief Default constructs the null object and then constructs the adapted object with the null object.
243
                 * \attention The behaviour is undefined if after the construction adapted does not compare equal to the null object.
244
                 */
245
                [[nodiscard]]
246
                explicit
247
                constexpr adapter(
×
248
                        [[maybe_unused]] adapter_null_t
249
                )
250
                        noexcept(std::is_nothrow_default_constructible_v<null_type>
251
                                        && std::is_nothrow_constructible_v<adapted_type, null_type&>)
252
                        requires std::default_initializable<null_type>
253
                                        && std::constructible_from<adapted_type, null_type&>
254
                        : adapter{in_place_null, null_type{}}
×
255
                {
256
                }
257

258
                /**
259
                 * \copydoc adapter(in_place_null_t, TNullArg&&)
260
                 */
261
                template <concepts::initializes<null_type> TNullArg>
262
                        requires concepts::not_same_as<adapter_null_t, std::remove_cvref_t<TNullArg>>
263
                                        && concepts::not_same_as<in_place_null_t, std::remove_cvref_t<TNullArg>>
264
                                        && (!detail::convertible_to_adapter<TNullArg>)
265
                                        && std::constructible_from<adapted_type, const null_type&>
266
                [[nodiscard]]
267
                explicit
268
                constexpr adapter(
3✔
269
                        TNullArg&& nullArg
270
                )
271
                        noexcept(std::is_nothrow_constructible_v<adapter, std::in_place_t, TNullArg>)
272
                        : adapter{in_place_null, nullArg}
3✔
273
                {
274
                }
3✔
275

276
                /**
277
                 * \brief Constructs the null object and the adapted with the given arguments.
278
                 * \tparam TNullArg The constructor argument type for the null object.
279
                 * \tparam TAdaptedArg The constructor argument type for the adapted object.
280
                 * \param nullArg  The constructor argument for the null object.
281
                 * \param adaptedArg  The constructor argument for the adapted object.
282
                 */
283
                template <concepts::initializes<null_type> TNullArg, concepts::initializes<adapted_type> TAdaptedArg>
284
                        requires concepts::not_same_as<adapter_null_t, std::remove_cvref_t<TNullArg>>
285
                                        && concepts::not_same_as<in_place_null_t, std::remove_cvref_t<TNullArg>>
286
                                        && concepts::not_same_as<adapter_null_t, std::remove_cvref_t<TAdaptedArg>>
287
                                        && concepts::not_same_as<in_place_null_t, std::remove_cvref_t<TAdaptedArg>>
288
                [[nodiscard]]
289
                constexpr adapter(
30✔
290
                        TNullArg&& nullArg,
291
                        TAdaptedArg&& adaptedArg
292
                )
293
                        noexcept(std::is_nothrow_constructible_v<null_type, TNullArg>
294
                                        && std::is_nothrow_constructible_v<adapted_type, TAdaptedArg>)
295
                        : m_Null{std::forward<TNullArg>(nullArg)},
30✔
296
                        m_Adapted{std::forward<TAdaptedArg>(adaptedArg)}
30✔
297
                {
298
                }
30✔
299

300
                /**
301
                 * \brief Constructs the adapter with the result of a ``to_nullables_adapter`` invocation.
302
                 * \tparam TArg The argument type.
303
                 * \param arg The argument.
304
                 * \note This function makes use of the customization point ``to_nullables_adapter``, which may be hooked by users.
305
                 */
306
                template <detail::convertible_to_adapter TArg>
307
                        requires concepts::not_same_as<adapter_null_t, std::remove_cvref_t<TArg>>
308
                                        && concepts::not_same_as<in_place_null_t, std::remove_cvref_t<TArg>>
309
                [[nodiscard]]
310
                explicit
311
                constexpr adapter(
6✔
312
                        TArg&& arg
313
                )
314
                        noexcept(noexcept(to_nullables_adapter(std::forward<TArg>(arg)))
315
                                        && std::is_nothrow_constructible_v<adapter>)
316
                        : adapter{to_nullables_adapter(std::forward<TArg>(arg))}
6✔
317
                {
318
                }
6✔
319

320
                /**
321
                 * \brief Assigns null to the adapted object.
322
                 * \attention The behaviour is undefined if adapted does not compare equal to the null object after the assignment.
323
                 * \return Reference to this.
324
                 */
325
                constexpr adapter& operator =(
1✔
326
                        [[maybe_unused]] adapter_null_t
327
                )
328
                        noexcept(std::is_nothrow_assignable_v<adapted_type&, null_type&>)
329
                        requires std::assignable_from<adapted_type&, null_type&>
330
                {
331
                        m_Adapted = m_Null;
1✔
332
                        assert(*this == adapter_null && "adapted_type must comapare equally to the null object.");
333

334
                        return *this;
1✔
335
                }
336

337
                /**
338
                 * \brief Assigns the adapted object.
339
                 * \return Reference to this.
340
                 */
341
                template <concepts::assignable_to<adapted_type&> TAdaptedArg>
342
                        requires concepts::not_same_as<adapter_null_t, std::remove_cvref_t<TAdaptedArg>>
343
                constexpr adapter& operator =(
3✔
344
                        TAdaptedArg&& adaptedArg
345
                )
346
                        noexcept(std::is_nothrow_assignable_v<adapted_type&, TAdaptedArg>)
347
                {
348
                        m_Adapted = std::forward<TAdaptedArg>(adaptedArg);
3✔
349

350
                        return *this;
3✔
351
                }
352

353
                /**
354
                 * \brief Retrieves the value from the adapted object.
355
                 * \note This function makes use of the ``unwrap_adapter`` customization point, which may be hooked by users.
356
                 * \attention If the adapted object compares equal to the null object, the behaviour is undefined.
357
                 * \return Returns the result as received.
358
                 */
359
                [[nodiscard]]
360
                constexpr decltype(auto) operator *() const &
2✔
361
                {
362
                        assert(*this != adapter_null && "Dereferenced adapted nullable is equally to its null.");
363

364
                        return unwrap_adapted(m_Adapted);
2✔
365
                }
366

367
                /**
368
                 * \copydoc operator*
369
                 */
370
                [[nodiscard]]
371
                constexpr decltype(auto) operator *() &
6✔
372
                {
373
                        assert(*this != adapter_null && "Dereferenced adapted nullable is equally to its null.");
374

375
                        return unwrap_adapted(m_Adapted);
6✔
376
                }
377

378
                /**
379
                 * \copydoc operator*
380
                 */
381
                [[nodiscard]]
382
                constexpr decltype(auto) operator *() &&
8✔
383
                {
384
                        assert(*this != adapter_null && "Dereferenced adapted nullable is equally to its null.");
385

386
                        return unwrap_adapted(std::move(m_Adapted));
8✔
387
                }
388

389
                /**
390
                 * \brief Comparison operator with its ``adapter_null```object.
391
                 * \return Returns true if the adapted object compares equal to the null object.
392
                 */
393
                [[nodiscard]]
394
                constexpr bool operator ==(
34✔
395
                        adapter_null_t
396
                ) const
397
                        noexcept(concepts::nothrow_weakly_equality_comparable_with<TAdapted, TNull>)
398
                {
399
                        return m_Adapted == m_Null;
34✔
400
                }
401

402
        private:
403
                SL_UTILITY_NO_UNIQUE_ADDRESS
404
                null_type m_Null{};
405
                adapted_type m_Adapted{};
406
        };
407

408
        /**
409
         * \brief Deduction guide.
410
         * \relates sl::nullables::adapter
411
         * \tparam TNull The null type.
412
         * \tparam TAdapted The adapted type.
413
         */
414
        template <class TNull, class TAdapted>
415
        adapter(TNull, TAdapted) -> adapter<TNull, TAdapted>;
416

417
        /**
418
         * \brief Deduction guide, which makes use of ``to_nullables_adapter`` customization point.
419
         * \relates sl::nullables::adapter
420
         * \tparam T The argument type.
421
         */
422
        template <class T>
423
                requires requires { to_nullables_adapter(std::declval<T>()); }
424
        adapter(
425
                T&& t
426
        ) -> adapter<
427
                typename std::remove_cvref_t<decltype(to_nullables_adapter(std::declval<T>()))>::null_type,
428
                typename std::remove_cvref_t<decltype(to_nullables_adapter(std::declval<T>()))>::adapted_type
429
        >;
430

431
        /**
432
         * \brief Specialization for raw pointers
433
         * \ingroup GROUP_NULLABLES_TRAITS
434
         * \relates sl::nullables::adapter
435
         * \tparam TNull Null type
436
         * \tparam TAdapted Adapted type
437
         */
438
        template <class TNull, class TAdapted>
439
        struct traits<adapter<TNull, TAdapted>>
440
        {
441
                using value_type = adapted_value_t<TAdapted>;
442
                constexpr static adapter_null_t null{adapter_null};
443
        };
444

445
        /** @} */
446
}
447

448
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc