Skip to content

Commit d4b4d38

Browse files
Constrain dyn constructors based on storage
1 parent fe323f4 commit d4b4d38

6 files changed

Lines changed: 44 additions & 31 deletions

File tree

include/simply/dyn.hpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,24 @@ class dyn
3131

3232
// TODO add default constructor for nullable mixin
3333

34-
// TODO generalize member constraints based on storage
3534
dyn(const dyn &other) = delete;
3635
constexpr dyn(const dyn &other)
37-
requires simply::copy_mixin<Mixin>
36+
requires simply::copy_constructible<storage_base>
3837
= default;
3938

4039
dyn(dyn &&other) = delete;
41-
constexpr dyn(dyn &&other) noexcept
42-
requires simply::move_mixin<Mixin>
40+
constexpr dyn(dyn &&other)
41+
requires simply::move_constructible<storage_base>
4342
= default;
4443

45-
template <typename Alloc, simply::has_mixin<Mixin> T, typename... Args>
44+
template <typename Alloc, simply::has_mixin<mixin_type> T, typename... Args>
4645
requires simply::constructible_from<T, Args...>
4746
constexpr explicit dyn(std::allocator_arg_t alloc_tag, const Alloc &alloc,
4847
std::in_place_type_t<T> obj_tag, Args &&...args)
4948
: storage_base(alloc_tag, alloc, obj_tag, std::forward<Args>(args)...),
5049
dispatch_base(obj_tag) {}
5150

52-
template <simply::has_mixin<Mixin> T, typename... Args>
51+
template <simply::has_mixin<mixin_type> T, typename... Args>
5352
requires simply::constructible_from<T, Args...>
5453
constexpr explicit dyn(std::in_place_type_t<T> tag, Args &&...args)
5554
: storage_base(tag, std::forward<Args>(args)...), dispatch_base(tag) {}
@@ -58,7 +57,7 @@ class dyn
5857
requires(not std::same_as<std::remove_cvref_t<T>, dyn>) and
5958
(not simply::specialization_of<std::remove_cvref_t<T>,
6059
std::in_place_type_t>) and
61-
simply::has_mixin<std::decay_t<T>, Mixin> and
60+
simply::has_mixin<std::decay_t<T>, mixin_type> and
6261
simply::constructible_from<std::decay_t<T>, T>
6362
constexpr explicit dyn(T &&value)
6463
: storage_base(std::in_place_type<std::decay_t<T>>,
@@ -67,7 +66,8 @@ class dyn
6766

6867
auto operator=(const dyn &other) -> dyn & = delete;
6968
constexpr auto operator=(const dyn &other) -> dyn &
70-
requires simply::destroy_mixin<Mixin> and simply::copy_mixin<Mixin>
69+
requires simply::destroy_mixin<mixin_type> and
70+
simply::copy_constructible<storage_base>
7171
{
7272
// prevent self-assignment
7373
if (this == std::addressof(other)) {
@@ -81,7 +81,8 @@ class dyn
8181

8282
auto operator=(dyn &&other) -> dyn & = delete;
8383
constexpr auto operator=(dyn &&other) noexcept -> dyn &
84-
requires simply::destroy_mixin<Mixin> and simply::move_mixin<Mixin>
84+
requires simply::destroy_mixin<mixin_type> and
85+
simply::move_constructible<storage_base>
8586
{
8687
std::destroy_at(this);
8788
std::construct_at(this, std::move(other));
@@ -92,9 +93,9 @@ class dyn
9293

9394
~dyn() = delete;
9495
constexpr ~dyn()
95-
requires simply::destroy_mixin<Mixin>
96+
requires simply::destroy_mixin<mixin_type>
9697
{
97-
using destroy = simply::destroy_mixin_t<Mixin>;
98+
using destroy = simply::destroy_mixin_t<mixin_type>;
9899
simply::fn<destroy, dyn>(*this);
99100
}
100101
};

include/simply/impl.hpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ inline constexpr const auto &fn = simply::impl<Mixin, T>::fn;
1919

2020
template <typename Mixin>
2121
struct _deduce_t {
22+
23+
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
24+
#define SIMPLY_PERFECT_FORWARD(...) \
25+
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { return __VA_ARGS__; }
26+
2227
template <typename T, typename... Args>
23-
static constexpr auto operator()(T &&self, Args &&...args) noexcept(noexcept(
24-
simply::fn<Mixin, std::remove_cvref_t<T>>(std::forward<T>(self),
25-
std::forward<Args>(args)...)))
26-
-> decltype(simply::fn<Mixin, std::remove_cvref_t<T>>(
27-
std::forward<T>(self), std::forward<Args>(args)...)) {
28-
return simply::fn<Mixin, std::remove_cvref_t<T>>(
29-
std::forward<T>(self), std::forward<Args>(args)...);
30-
}
28+
static constexpr auto operator()(T &&self, Args &&...args)
29+
SIMPLY_PERFECT_FORWARD(simply::fn<Mixin, std::remove_cvref_t<T>>(
30+
std::forward<T>(self), std::forward<Args>(args)...))
31+
32+
#undef SIMPLY_PERFECT_FORWARD
3133
};
3234

3335
template <typename Mixin>

include/simply/storage.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,13 @@ struct impl<simply::impl<Copy, T>, simply::iface<Storage, Dyn>,
101101
}
102102

103103
private:
104-
impl() noexcept = default;
104+
impl() = default;
105105
};
106106

107107
template <simply::specialization_of<simply::allocator_storage> Storage,
108108
typename Self>
109109
requires simply::mixin<Storage>
110+
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
110111
struct iface<Storage, Self> {
111112
private:
112113
using traits = Storage::traits;
@@ -127,6 +128,13 @@ struct iface<Storage, Self> {
127128
requires std::default_initializable<void_pointer>
128129
: alloc(alloc), object_ptr() {}
129130

131+
// copy constructor overload resolution needs to consider the template below
132+
iface(const iface &)
133+
requires false;
134+
135+
// defer instantiation until Self::mixin_type is accessible
136+
template <typename T = Self>
137+
requires simply::copy_mixin<typename T::mixin_type>
130138
constexpr iface(const iface &other) {
131139
using copy = simply::copy_mixin_t<typename Self::mixin_type>;
132140

@@ -138,6 +146,7 @@ struct iface<Storage, Self> {
138146
}));
139147
}
140148

149+
// allocator_storage can implement move construction without type erasure
141150
constexpr iface(iface &&other) noexcept
142151
: alloc(other.alloc), object_ptr(other._release()) {}
143152

@@ -159,7 +168,7 @@ struct iface<Storage, Self> {
159168
std::forward<Args>(args)...)) {}
160169

161170
auto operator=(const iface &other) -> iface & = default;
162-
auto operator=(iface &&other) noexcept -> iface & = default;
171+
auto operator=(iface &&other) -> iface & = default;
163172

164173
~iface() = default;
165174

tests/concepts.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ static_assert(constructible_from_in_place<unusable, immovable>);
6262
static_assert(constructible_from_in_place<unusable, indestructible>);
6363

6464
static_assert(not simply::copy_constructible<unusable>);
65-
static_assert(not simply::move_constructible<unusable>);
65+
// move constructible because allocator_storage
66+
static_assert(simply::move_constructible<unusable>);
6667
static_assert(not std::is_destructible_v<unusable>);
6768

6869
static_assert(constructible_from_in_place<copyable, semiregular>);
@@ -75,7 +76,8 @@ static_assert(constructible_from_in_place<copyable, indestructible>);
7576
#endif
7677

7778
static_assert(simply::copy_constructible<copyable>);
78-
static_assert(not simply::move_constructible<copyable>);
79+
// move constructible because allocator_storage
80+
static_assert(simply::move_constructible<copyable>);
7981
static_assert(not std::is_destructible_v<copyable>);
8082

8183
static_assert(constructible_from_in_place<movable, semiregular>);
@@ -97,5 +99,6 @@ static_assert(constructible_from_in_place<destructible, immovable>);
9799
static_assert(not constructible_from_in_place<destructible, indestructible>);
98100

99101
static_assert(not simply::copy_constructible<destructible>);
100-
static_assert(not simply::move_constructible<destructible>);
102+
// move constructible because allocator_storage
103+
static_assert(simply::move_constructible<destructible>);
101104
static_assert(std::is_destructible_v<destructible>);

tests/counters.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#include <simply/copyable.hpp>
22
#include <simply/destructible.hpp>
33
#include <simply/dyn.hpp>
4-
#include <simply/movable.hpp>
54

65
#include <algorithm>
76
#include <cassert>
@@ -12,7 +11,7 @@ template <char Label>
1211
struct counter {
1312
counter() = default;
1413
constexpr counter(const counter &other) : copied(other.copied + 1) {}
15-
counter(counter &&other) noexcept = default;
14+
counter(counter &&other) = delete;
1615

1716
auto operator=(const counter &) -> counter & = delete;
1817
auto operator=(counter &&) -> counter & = delete;
@@ -61,9 +60,9 @@ struct simply::iface<labeled<LabelT>, Self> {
6160

6261
// test dyn<countable> at compile-time
6362
static_assert([] {
64-
struct countable
65-
: simply::composes<copy_countable<int>, labeled<char>, simply::copyable,
66-
simply::movable, simply::destructible> {};
63+
struct countable : simply::composes<copy_countable<int>, labeled<char>,
64+
simply::copyable, simply::destructible> {
65+
};
6766

6867
// initialize a vector of dyn<countable> with three different counter types
6968
std::vector<simply::dyn<countable>> v;

tests/stream.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#include <simply/destructible.hpp>
22
#include <simply/dyn.hpp>
33
#include <simply/insertable.hpp>
4-
#include <simply/movable.hpp>
54

65
#include <gtest/gtest.h>
76

@@ -11,7 +10,7 @@ TEST(Stream, TypeErasedInsertion) {
1110
using namespace std::string_literals;
1211

1312
struct mixins : simply::composes<simply::insertable<std::ostream>,
14-
simply::movable, simply::destructible> {};
13+
simply::destructible> {};
1514

1615
std::vector<simply::dyn<mixins>> values;
1716
values.emplace_back("Hello, world!"s);

0 commit comments

Comments
 (0)