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

bemanproject / indirect / 24747050000

21 Apr 2026 09:20PM UTC coverage: 92.256% (+0.03%) from 92.23%
24747050000

push

github

web-flow
Merge pull request #9 from bemanproject/constexpr-polymorphic

Make polymorphic constexpr and add constexpr tests

11 of 11 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

274 of 297 relevant lines covered (92.26%)

22.78 hits per line

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

89.44
/include/beman/indirect/polymorphic.hpp
1
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2

3
#ifndef BEMAN_INDIRECT_POLYMORPHIC_HPP
4
#define BEMAN_INDIRECT_POLYMORPHIC_HPP
5

6
#include <beman/indirect/detail/config.hpp>
7
#include <beman/indirect/detail/synth_three_way.hpp>
8

9
#include <cassert>
10
#include <initializer_list>
11
#include <memory>
12
#include <memory_resource>
13
#include <type_traits>
14
#include <utility>
15

16
namespace beman::indirect {
17

18
namespace detail {
19

20
// Check if U is a specialization of in_place_type_t.
21
template <class>
22
inline constexpr bool is_in_place_type_v = false;
23

24
template <class T>
25
inline constexpr bool is_in_place_type_v<std::in_place_type_t<T>> = true;
26

27
// Abstract control block for type-erased polymorphic storage.
28
template <class T, class Allocator>
29
struct control_block {
30
    T* p_;
31

32
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL virtual control_block* clone(const Allocator& alloc) const = 0;
33
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL virtual control_block* move_clone(const Allocator& alloc)  = 0;
34
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL virtual void           destroy(Allocator& alloc) noexcept  = 0;
35

36
  protected:
37
    BEMAN_INDIRECT_CONSTEXPR_DTOR ~control_block() = default;
38
};
39

40
// Concrete control block that stores a value of type U (derived from T) inline.
41
template <class T, class U, class Allocator>
42
struct direct_control_block final : control_block<T, Allocator> {
43
    using cb_alloc  = typename std::allocator_traits<Allocator>::template rebind_alloc<direct_control_block>;
44
    using cb_traits = std::allocator_traits<cb_alloc>;
45

46
    union storage {
47
        U value;
48
        constexpr storage() {}
96✔
49
        BEMAN_INDIRECT_CONSTEXPR_DTOR ~storage() {}
96✔
50
    } storage_;
51

52
    template <class... Args>
53
    constexpr explicit direct_control_block(const Allocator& alloc, Args&&... args) {
96✔
54
        Allocator a(alloc);
55
        std::allocator_traits<Allocator>::construct(a, std::addressof(storage_.value), std::forward<Args>(args)...);
193✔
56
        this->p_ = std::addressof(storage_.value);
166✔
57
    }
96✔
58

26✔
59
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL control_block<T, Allocator>* clone(const Allocator& alloc) const override {
16✔
60
        cb_alloc a(alloc);
61
        auto*    mem = cb_traits::allocate(a, 1);
16✔
62
        try {
63
            construct_at_impl(mem, alloc, storage_.value);
16✔
64
        } catch (...) {
×
65
            cb_traits::deallocate(a, mem, 1);
66
            throw;
×
67
        }
68
        return mem;
27✔
69
    }
5✔
70

×
71
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL control_block<T, Allocator>* move_clone(const Allocator& alloc) override {
1✔
72
        cb_alloc a(alloc);
73
        auto*    mem = cb_traits::allocate(a, 1);
1✔
74
        try {
75
            construct_at_impl(mem, alloc, std::move(storage_.value));
2✔
76
        } catch (...) {
×
77
            cb_traits::deallocate(a, mem, 1);
78
            throw;
×
79
        }
80
        return mem;
1✔
81
    }
1✔
82

×
83
    BEMAN_INDIRECT_CONSTEXPR_VIRTUAL void destroy(Allocator& alloc) noexcept override {
96✔
84
        cb_alloc a(alloc);
85
        std::destroy_at(std::addressof(storage_.value));
192✔
86
        cb_traits::destroy(a, this);
87
        cb_traits::deallocate(a, this, 1);
88
    }
96✔
89
};
26✔
90

26✔
91
} // namespace detail
26✔
92

93
// [polymorphic] Class template polymorphic
94
// N5032 ยง20.4.2
95
template <class T, class Allocator = std::allocator<T>>
96
class polymorphic {
97
    static_assert(std::is_object_v<T>, "T must be an object type");
98
    static_assert(!std::is_array_v<T>, "T must not be an array type");
99
    static_assert(!std::is_const_v<T> && !std::is_volatile_v<T>, "T must not be cv-qualified");
100
    static_assert(std::is_same_v<T, typename std::allocator_traits<Allocator>::value_type>,
101
                  "Allocator::value_type must be T");
102

103
    using alloc_traits = std::allocator_traits<Allocator>;
104
    using cb_type      = detail::control_block<T, Allocator>;
105

106
    template <class U>
107
    using direct_cb = detail::direct_control_block<T, U, Allocator>;
108

109
    template <class U>
110
    using cb_alloc = typename alloc_traits::template rebind_alloc<direct_cb<U>>;
111

112
    template <class U>
113
    using cb_traits = std::allocator_traits<cb_alloc<U>>;
114

115
  public:
116
    using value_type     = T;
117
    using allocator_type = Allocator;
118
    using pointer        = typename alloc_traits::pointer;
119
    using const_pointer  = typename alloc_traits::const_pointer;
120

121
    // [polymorphic.ctor] constructors
122

123
#if BEMAN_INDIRECT_USE_CONCEPTS
124
    constexpr explicit polymorphic()
1✔
125
        requires std::is_default_constructible_v<Allocator>
126
    {
1✔
127
#else
128
    template <class Alloc_ = Allocator, std::enable_if_t<std::is_default_constructible_v<Alloc_>, int> = 0>
129
    constexpr explicit polymorphic() {
130
#endif
131
        static_assert(std::is_default_constructible_v<T>);
132
        static_assert(std::is_copy_constructible_v<T>);
133
        cb_ = make_cb<T>(alloc_);
1✔
134
    }
1✔
135

136
    constexpr explicit polymorphic(std::allocator_arg_t, const Allocator& a) : alloc_(a) {
2✔
137
        static_assert(std::is_default_constructible_v<T>);
138
        static_assert(std::is_copy_constructible_v<T>);
139
        cb_ = make_cb<T>(alloc_);
2✔
140
    }
2✔
141

142
    constexpr polymorphic(const polymorphic& other)
12✔
143
        : alloc_(alloc_traits::select_on_container_copy_construction(other.alloc_)) {
12✔
144
        if (!other.valueless_after_move()) {
12✔
145
            cb_ = other.cb_->clone(alloc_);
11✔
146
        }
147
    }
12✔
148

149
    constexpr polymorphic(std::allocator_arg_t, const Allocator& a, const polymorphic& other) : alloc_(a) {
2✔
150
        if (!other.valueless_after_move()) {
2✔
151
            cb_ = other.cb_->clone(alloc_);
2✔
152
        }
153
    }
2✔
154

155
    constexpr polymorphic(polymorphic&& other) noexcept : alloc_(std::move(other.alloc_)), cb_(other.cb_) {
45✔
156
        other.cb_ = nullptr;
23✔
157
    }
23✔
158

159
    constexpr polymorphic(std::allocator_arg_t,
2✔
160
                          const Allocator& a,
161
                          polymorphic&&    other) noexcept(alloc_traits::is_always_equal::value)
162
        : alloc_(a) {
2✔
163
        if (other.valueless_after_move()) {
2✔
164
            // *this is valueless
165
        } else if constexpr (alloc_traits::is_always_equal::value) {
166
            cb_       = other.cb_;
167
            other.cb_ = nullptr;
168
        } else {
169
            if (alloc_ == other.alloc_) {
1✔
170
                cb_       = other.cb_;
×
171
                other.cb_ = nullptr;
×
172
            } else {
173
                cb_ = other.cb_->move_clone(alloc_);
1✔
174
                other.reset();
1✔
175
            }
176
        }
177
    }
2✔
178

1✔
179
#if BEMAN_INDIRECT_USE_CONCEPTS
1✔
180
    template <class U = T>
181
        requires(!std::is_same_v<detail::remove_cvref_t<U>, polymorphic> &&
182
                 detail::derived_from_v<detail::remove_cvref_t<U>, T> &&
183
                 std::is_constructible_v<detail::remove_cvref_t<U>, U> &&
184
                 std::is_copy_constructible_v<detail::remove_cvref_t<U>> &&
185
                 !detail::is_in_place_type_v<detail::remove_cvref_t<U>> && std::is_default_constructible_v<Allocator>)
186
    constexpr explicit polymorphic(U&& u) {
100✔
187
#else
188
    template <class U               = T,
189
              std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<U>, polymorphic> &&
190
                                   detail::derived_from_v<detail::remove_cvref_t<U>, T> &&
191
                                   std::is_constructible_v<detail::remove_cvref_t<U>, U> &&
192
                                   std::is_copy_constructible_v<detail::remove_cvref_t<U>> &&
193
                                   !detail::is_in_place_type_v<detail::remove_cvref_t<U>> &&
194
                                   std::is_default_constructible_v<Allocator>,
195
                               int> = 0>
196
    constexpr explicit polymorphic(U&& u) {
197
#endif
198
        cb_ = make_cb<detail::remove_cvref_t<U>>(alloc_, std::forward<U>(u));
50✔
199
    }
50✔
200

201
#if BEMAN_INDIRECT_USE_CONCEPTS
202
    template <class U = T>
203
        requires(!std::is_same_v<detail::remove_cvref_t<U>, polymorphic> &&
204
                 detail::derived_from_v<detail::remove_cvref_t<U>, T> &&
205
                 std::is_constructible_v<detail::remove_cvref_t<U>, U> &&
206
                 std::is_copy_constructible_v<detail::remove_cvref_t<U>> &&
207
                 !detail::is_in_place_type_v<detail::remove_cvref_t<U>>)
208
    constexpr explicit polymorphic(std::allocator_arg_t, const Allocator& a, U&& u) : alloc_(a) {
20✔
209
#else
210
    template <class U               = T,
211
              std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<U>, polymorphic> &&
212
                                   detail::derived_from_v<detail::remove_cvref_t<U>, T> &&
213
                                   std::is_constructible_v<detail::remove_cvref_t<U>, U> &&
214
                                   std::is_copy_constructible_v<detail::remove_cvref_t<U>> &&
215
                                   !detail::is_in_place_type_v<detail::remove_cvref_t<U>>,
216
                               int> = 0>
217
    constexpr explicit polymorphic(std::allocator_arg_t, const Allocator& a, U&& u) : alloc_(a) {
218
#endif
219
        cb_ = make_cb<detail::remove_cvref_t<U>>(alloc_, std::forward<U>(u));
19✔
220
    }
19✔
221

222
#if BEMAN_INDIRECT_USE_CONCEPTS
223
    template <class U, class... Ts>
224
        requires(std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
225
                 std::is_constructible_v<U, Ts...> && std::is_copy_constructible_v<U> &&
226
                 std::is_default_constructible_v<Allocator>)
227
    constexpr explicit polymorphic(std::in_place_type_t<U>, Ts&&... ts) {
4✔
228
#else
229
    template <class U,
230
              class... Ts,
231
              std::enable_if_t<std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
232
                                   std::is_constructible_v<U, Ts...> && std::is_copy_constructible_v<U> &&
233
                                   std::is_default_constructible_v<Allocator>,
234
                               int> = 0>
235
    constexpr explicit polymorphic(std::in_place_type_t<U>, Ts&&... ts) {
236
#endif
237
        cb_ = make_cb<U>(alloc_, std::forward<Ts>(ts)...);
2✔
238
    }
2✔
239

240
#if BEMAN_INDIRECT_USE_CONCEPTS
241
    template <class U, class... Ts>
242
        requires(std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
243
                 std::is_constructible_v<U, Ts...> && std::is_copy_constructible_v<U>)
244
    constexpr explicit polymorphic(std::allocator_arg_t, const Allocator& a, std::in_place_type_t<U>, Ts&&... ts)
2✔
245
        : alloc_(a) {
3✔
246
#else
247
    template <class U,
248
              class... Ts,
249
              std::enable_if_t<std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
250
                                   std::is_constructible_v<U, Ts...> && std::is_copy_constructible_v<U>,
251
                               int> = 0>
252
    constexpr explicit polymorphic(std::allocator_arg_t, const Allocator& a, std::in_place_type_t<U>, Ts&&... ts)
253
        : alloc_(a) {
254
#endif
255
        cb_ = make_cb<U>(alloc_, std::forward<Ts>(ts)...);
2✔
256
    }
2✔
257

258
#if BEMAN_INDIRECT_USE_CONCEPTS
259
    template <class U, class I, class... Us>
260
        requires(std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
261
                 std::is_constructible_v<U, std::initializer_list<I>&, Us...> && std::is_copy_constructible_v<U> &&
262
                 std::is_default_constructible_v<Allocator>)
263
    constexpr explicit polymorphic(std::in_place_type_t<U>, std::initializer_list<I> ilist, Us&&... us) {
3✔
264
#else
265
    template <class U,
266
              class I,
267
              class... Us,
268
              std::enable_if_t<std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
269
                                   std::is_constructible_v<U, std::initializer_list<I>&, Us...> &&
270
                                   std::is_copy_constructible_v<U> && std::is_default_constructible_v<Allocator>,
271
                               int> = 0>
272
    constexpr explicit polymorphic(std::in_place_type_t<U>, std::initializer_list<I> ilist, Us&&... us) {
273
#endif
274
        cb_ = make_cb<U>(alloc_, ilist, std::forward<Us>(us)...);
2✔
275
    }
2✔
276

277
#if BEMAN_INDIRECT_USE_CONCEPTS
278
    template <class U, class I, class... Us>
279
        requires(std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
280
                 std::is_constructible_v<U, std::initializer_list<I>&, Us...> && std::is_copy_constructible_v<U>)
281
    constexpr explicit polymorphic(
1✔
282
        std::allocator_arg_t, const Allocator& a, std::in_place_type_t<U>, std::initializer_list<I> ilist, Us&&... us)
283
        : alloc_(a) {
1✔
284
#else
285
    template <class U,
286
              class I,
287
              class... Us,
288
              std::enable_if_t<std::is_same_v<detail::remove_cvref_t<U>, U> && detail::derived_from_v<U, T> &&
289
                                   std::is_constructible_v<U, std::initializer_list<I>&, Us...> &&
290
                                   std::is_copy_constructible_v<U>,
291
                               int> = 0>
292
    constexpr explicit polymorphic(
293
        std::allocator_arg_t, const Allocator& a, std::in_place_type_t<U>, std::initializer_list<I> ilist, Us&&... us)
294
        : alloc_(a) {
295
#endif
296
        cb_ = make_cb<U>(alloc_, ilist, std::forward<Us>(us)...);
1✔
297
    }
1✔
298

299
    // [polymorphic.dtor] destructor
300

301
    BEMAN_INDIRECT_CONSTEXPR_DTOR ~polymorphic() {
118✔
302
        static_assert(detail::is_complete_v<T>);
303
        reset();
118✔
304
    }
118✔
305

306
    // [polymorphic.assign] assignment
307

308
    constexpr polymorphic& operator=(const polymorphic& other) {
5✔
309
        static_assert(detail::is_complete_v<T>);
310
        if (std::addressof(other) == this)
5✔
311
            return *this;
1✔
312

313
        constexpr bool pocca                  = alloc_traits::propagate_on_container_copy_assignment::value;
4✔
314
        Allocator      alloc_for_construction = pocca ? other.alloc_ : alloc_;
4✔
315

316
        cb_type* new_cb = nullptr;
4✔
317
        if (!other.valueless_after_move()) {
4✔
318
            new_cb = other.cb_->clone(alloc_for_construction);
3✔
319
        }
320
        reset();
4✔
321
        cb_ = new_cb;
4✔
322

323
        if constexpr (pocca) {
324
            alloc_ = other.alloc_;
325
        }
326
        return *this;
4✔
327
    }
328

329
    constexpr polymorphic&
330
    operator=(polymorphic&& other) noexcept(alloc_traits::propagate_on_container_move_assignment::value ||
6✔
331
                                            alloc_traits::is_always_equal::value) {
332
        static_assert(alloc_traits::propagate_on_container_move_assignment::value ||
333
                          alloc_traits::is_always_equal::value || detail::is_complete_v<T>,
334
                      "T must be complete when allocators may not be equal");
335
        if (std::addressof(other) == this)
6✔
336
            return *this;
1✔
337

338
        constexpr bool pocma = alloc_traits::propagate_on_container_move_assignment::value;
5✔
339

340
        if (other.valueless_after_move()) {
5✔
341
            reset();
1✔
342
        } else if (pocma || alloc_ == other.alloc_) {
343
            reset();
4✔
344
            cb_       = other.cb_;
4✔
345
            other.cb_ = nullptr;
4✔
346
        } else {
347
            cb_type* new_cb = other.cb_->move_clone(alloc_);
348
            reset();
349
            cb_ = new_cb;
350
            other.reset();
351
        }
352

353
        if constexpr (pocma) {
354
            alloc_ = other.alloc_;
1✔
355
        }
356
        return *this;
5✔
357
    }
1✔
358

×
359
    // [polymorphic.obs] observers
×
360

×
361
    constexpr const T& operator*() const noexcept {
×
362
        assert(!valueless_after_move());
1✔
363
        return *(cb_->p_);
1✔
364
    }
1✔
365

366
    constexpr T& operator*() noexcept {
65✔
367
        assert(!valueless_after_move());
65✔
368
        return *(cb_->p_);
65✔
369
    }
370

371
    constexpr const_pointer operator->() const noexcept {
1✔
372
        assert(!valueless_after_move());
1✔
373
        return std::pointer_traits<const_pointer>::pointer_to(*(cb_->p_));
1✔
374
    }
375

376
    constexpr pointer operator->() noexcept {
2✔
377
        assert(!valueless_after_move());
2✔
378
        return std::pointer_traits<pointer>::pointer_to(*(cb_->p_));
2✔
379
    }
380

381
    constexpr bool valueless_after_move() const noexcept { return cb_ == nullptr; }
120✔
382

383
    constexpr allocator_type get_allocator() const noexcept { return alloc_; }
2✔
384

385
    // [polymorphic.swap] swap
386

387
    constexpr void swap(polymorphic& other) noexcept(alloc_traits::propagate_on_container_swap::value ||
7✔
388
                                                     alloc_traits::is_always_equal::value) {
389
        // Precondition: allocators must be equal when they don't propagate on swap.
390
        assert(alloc_traits::propagate_on_container_swap::value || alloc_ == other.alloc_);
391
        using std::swap;
392
        swap(cb_, other.cb_);
7✔
393
        if constexpr (alloc_traits::propagate_on_container_swap::value) {
394
            swap(alloc_, other.alloc_);
1✔
395
        }
396
    }
7✔
397

12✔
398
    friend constexpr void swap(polymorphic& lhs, polymorphic& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); }
1✔
399

400
  private:
401
    template <class U, class... Args>
402
    BEMAN_INDIRECT_CONSTEXPR_DTOR static cb_type* make_cb(Allocator& alloc, Args&&... args) {
79✔
403
        cb_alloc<U> a(alloc);
404
        auto*       mem = cb_traits<U>::allocate(a, 1);
79✔
405
        try {
406
            detail::construct_at_impl(mem, alloc, std::forward<Args>(args)...);
80✔
407
        } catch (...) {
×
408
            cb_traits<U>::deallocate(a, mem, 1);
409
            throw;
×
410
        }
411
        return mem;
138✔
412
    }
20✔
UNCOV
413

×
414
    constexpr void reset() {
128✔
415
        if (cb_) {
128✔
416
            cb_->destroy(alloc_);
96✔
417
            cb_ = nullptr;
96✔
418
        }
419
    }
128✔
420

421
    BEMAN_INDIRECT_NO_UNIQUE_ADDRESS Allocator alloc_ = Allocator();
422
    cb_type*                                   cb_    = nullptr;
423
};
424

425
} // namespace beman::indirect
426

427
namespace beman::indirect::pmr {
428

429
template <class T>
430
using polymorphic = beman::indirect::polymorphic<T, std::pmr::polymorphic_allocator<T>>;
431

432
} // namespace beman::indirect::pmr
433

434
#endif // BEMAN_INDIRECT_POLYMORPHIC_HPP
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