Skip to content

Commit cd5304c

Browse files
Exclude destruction from construction contraints
1 parent 7f06482 commit cd5304c

File tree

8 files changed

+158
-15
lines changed

8 files changed

+158
-15
lines changed

include/simply/concepts.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,33 @@ concept fundamental_destroy_mixin =
8181
template <typename T, typename U>
8282
concept different_from = not std::same_as<T, U>;
8383

84+
// std::is_constructible requires destruction to be well-formed
85+
template <typename T, typename... Args>
86+
concept constructible_from =
87+
requires(void *location) { ::new (location) T(std::declval<Args>()...); };
88+
89+
// std::is_nothrow_constructible requires destruction to be well-formed
90+
template <typename T, typename... Args>
91+
concept nothrow_constructible_from = requires(void *location) {
92+
{ ::new (location) T(std::declval<Args>()...) } noexcept;
93+
};
94+
95+
template <typename T>
96+
concept default_constructible = simply::constructible_from<T>;
97+
98+
template <typename T>
99+
concept copy_constructible = simply::constructible_from<T, const T &>;
100+
101+
template <typename T>
102+
concept nothrow_copy_constructible =
103+
simply::nothrow_constructible_from<T, const T &>;
104+
105+
template <typename T>
106+
concept move_constructible = simply::constructible_from<T, T>;
107+
108+
template <typename T>
109+
concept nothrow_move_constructible = simply::nothrow_constructible_from<T, T>;
110+
84111
template <simply::composition Composition, typename T>
85112
inline constexpr bool enable_mixin_for<Composition, T> =
86113
simply::enable_mixin_for<simply::unique_fundamental_t<Composition>, T>;

include/simply/copyable.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
#ifndef SIMPLY_COPYABLE_HPP
22
#define SIMPLY_COPYABLE_HPP
33

4-
#include <simply/type_traits.hpp>
4+
#include <simply/concepts.hpp>
55

66
namespace simply {
77

88
struct copyable : simply::copy_base {
9-
template <typename T>
10-
requires std::is_copy_constructible_v<T>
9+
template <simply::copy_constructible T>
1110
static constexpr auto fn(const std::type_identity_t<T> &self) noexcept(
12-
std::is_nothrow_copy_constructible_v<T>) -> T {
11+
simply::nothrow_copy_constructible<T>) -> T {
1312
return self;
1413
}
1514
};

include/simply/destructible.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
namespace simply {
99

1010
struct destructible : simply::destroy_base {
11-
template <std::destructible T>
12-
static constexpr void fn(std::type_identity_t<T> &self) noexcept {
11+
template <typename T>
12+
requires std::is_destructible_v<T>
13+
static constexpr void fn(std::type_identity_t<T> &self) noexcept(
14+
std::is_nothrow_destructible_v<T>) {
1315
if constexpr (not std::is_trivially_destructible_v<T>) {
1416
std::destroy_at(std::addressof(self));
1517
}

include/simply/dyn.hpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,36 +32,42 @@ class dyn
3232
// TODO add default constructor for nullable mixin
3333

3434
// TODO generalize member constraints based on storage
35+
dyn(const dyn &other) = delete;
3536
constexpr dyn(const dyn &other)
3637
requires simply::copy_mixin<Mixin>
3738
= default;
3839

39-
constexpr dyn(dyn &&other) noexcept = default;
40+
dyn(dyn &&other) = delete;
41+
constexpr dyn(dyn &&other) noexcept
42+
requires simply::move_mixin<Mixin>
43+
= default;
4044

4145
template <typename Alloc, simply::has_mixin<Mixin> T, typename... Args>
42-
requires std::constructible_from<T, Args...>
46+
requires simply::constructible_from<T, Args...>
4347
constexpr explicit dyn(std::allocator_arg_t alloc_tag, const Alloc &alloc,
4448
std::in_place_type_t<T> obj_tag, Args &&...args)
4549
: storage_base(alloc_tag, alloc, obj_tag, std::forward<Args>(args)...),
4650
dispatch_base(obj_tag) {}
4751

4852
template <simply::has_mixin<Mixin> T, typename... Args>
49-
requires std::constructible_from<T, Args...>
53+
requires simply::constructible_from<T, Args...>
5054
constexpr explicit dyn(std::in_place_type_t<T> tag, Args &&...args)
5155
: storage_base(tag, std::forward<Args>(args)...), dispatch_base(tag) {}
5256

5357
template <typename T>
5458
requires(not std::same_as<std::remove_cvref_t<T>, dyn>) and
5559
(not simply::specialization_of<std::remove_cvref_t<T>,
5660
std::in_place_type_t>) and
57-
simply::has_mixin<std::decay_t<T>, Mixin>
61+
simply::has_mixin<std::decay_t<T>, Mixin> and
62+
simply::constructible_from<std::decay_t<T>, T>
5863
constexpr explicit dyn(T &&value)
5964
: storage_base(std::in_place_type<std::decay_t<T>>,
6065
std::forward<T>(value)),
6166
dispatch_base(std::in_place_type<std::decay_t<T>>) {}
6267

68+
auto operator=(const dyn &other) -> dyn & = delete;
6369
constexpr auto operator=(const dyn &other) -> dyn &
64-
requires simply::copy_mixin<Mixin>
70+
requires simply::destroy_mixin<Mixin> and simply::copy_mixin<Mixin>
6571
{
6672
// prevent self-assignment
6773
if (this == std::addressof(other)) {
@@ -73,14 +79,18 @@ class dyn
7379
return *this;
7480
}
7581

76-
constexpr auto operator=(dyn &&other) noexcept -> dyn & {
82+
auto operator=(dyn &&other) -> dyn & = delete;
83+
constexpr auto operator=(dyn &&other) noexcept -> dyn &
84+
requires simply::destroy_mixin<Mixin> and simply::move_mixin<Mixin>
85+
{
7786
std::destroy_at(this);
7887
std::construct_at(this, std::move(other));
7988
return *this;
8089
}
8190

8291
// TODO expose overloads for assignment mixins
8392

93+
~dyn() = delete;
8494
constexpr ~dyn()
8595
requires simply::destroy_mixin<Mixin>
8696
{

include/simply/impl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ template <typename Mixin>
3434
inline constexpr simply::_deduce_t<Mixin> fn<Mixin, simply::_deduce_tag_t>{};
3535

3636
template <simply::member Mixin, typename T>
37+
requires requires { Mixin::template fn<T>; }
3738
struct mixin_traits<Mixin, T> {
3839
using function_type = decltype(Mixin::template fn<T>);
3940
};

include/simply/movable.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
#ifndef SIMPLY_MOVABLE_HPP
22
#define SIMPLY_MOVABLE_HPP
33

4-
#include <simply/type_traits.hpp>
4+
#include <simply/concepts.hpp>
55

66
#include <utility>
77

88
namespace simply {
99

1010
struct movable : simply::move_base {
11-
template <std::move_constructible T>
11+
template <simply::move_constructible T>
1212
static constexpr auto fn(std::type_identity_t<T> &&self) noexcept(
13-
std::is_nothrow_move_constructible_v<T>) -> T {
13+
simply::nothrow_move_constructible<T>) -> T {
1414
return std::move(self);
1515
}
1616
};

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ FetchContent_Declare(
88

99
FetchContent_MakeAvailable(googletest)
1010

11+
add_library(concepts concepts.cpp)
12+
target_link_libraries(concepts simply)
13+
1114
add_library(counters counters.cpp)
1215
target_link_libraries(counters simply)
1316

tests/concepts.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include <simply/copyable.hpp>
2+
#include <simply/destructible.hpp>
3+
#include <simply/dyn.hpp>
4+
#include <simply/movable.hpp>
5+
6+
struct semiregular {
7+
semiregular();
8+
semiregular(const semiregular &);
9+
semiregular(semiregular &&) noexcept;
10+
11+
auto operator=(const semiregular &) -> semiregular &;
12+
auto operator=(semiregular &&) noexcept -> semiregular &;
13+
14+
~semiregular();
15+
};
16+
17+
struct uncopyable {
18+
uncopyable();
19+
uncopyable(const uncopyable &) = delete;
20+
uncopyable(uncopyable &&) noexcept;
21+
22+
auto operator=(const uncopyable &) -> uncopyable &;
23+
auto operator=(uncopyable &&) noexcept -> uncopyable &;
24+
25+
~uncopyable();
26+
};
27+
28+
struct immovable {
29+
immovable();
30+
immovable(const immovable &);
31+
immovable(immovable &&) = delete;
32+
33+
auto operator=(const immovable &) -> immovable &;
34+
auto operator=(immovable &&) noexcept -> immovable &;
35+
36+
~immovable();
37+
};
38+
39+
struct indestructible {
40+
indestructible();
41+
indestructible(const indestructible &);
42+
indestructible(indestructible &&) noexcept;
43+
44+
auto operator=(const indestructible &) -> indestructible &;
45+
auto operator=(indestructible &&) noexcept -> indestructible &;
46+
47+
~indestructible() = delete;
48+
};
49+
50+
using unusable = simply::dyn<simply::composes<>>;
51+
using copyable = simply::dyn<simply::copyable>;
52+
using movable = simply::dyn<simply::movable>;
53+
using destructible = simply::dyn<simply::destructible>;
54+
55+
template <typename T, typename U>
56+
concept constructible_from_in_place =
57+
simply::constructible_from<T, std::in_place_type_t<U>>;
58+
59+
static_assert(constructible_from_in_place<unusable, semiregular>);
60+
static_assert(constructible_from_in_place<unusable, uncopyable>);
61+
static_assert(constructible_from_in_place<unusable, immovable>);
62+
static_assert(constructible_from_in_place<unusable, indestructible>);
63+
64+
static_assert(not simply::copy_constructible<unusable>);
65+
static_assert(not simply::move_constructible<unusable>);
66+
static_assert(not std::is_destructible_v<unusable>);
67+
68+
static_assert(constructible_from_in_place<copyable, semiregular>);
69+
static_assert(not constructible_from_in_place<copyable, uncopyable>);
70+
static_assert(constructible_from_in_place<copyable, immovable>);
71+
// TODO reimplement copyable not to depend on destruction
72+
// clang is the only compiler that thinks it doesn't
73+
#if defined(__clang__)
74+
static_assert(constructible_from_in_place<copyable, indestructible>);
75+
#endif
76+
77+
static_assert(simply::copy_constructible<copyable>);
78+
static_assert(not simply::move_constructible<copyable>);
79+
static_assert(not std::is_destructible_v<copyable>);
80+
81+
static_assert(constructible_from_in_place<movable, semiregular>);
82+
static_assert(constructible_from_in_place<movable, uncopyable>);
83+
static_assert(not constructible_from_in_place<movable, immovable>);
84+
// TODO reimplement movable not to depend on destruction
85+
// clang is the only compiler that thinks it doesn't
86+
#if defined(__clang__)
87+
static_assert(constructible_from_in_place<movable, indestructible>);
88+
#endif
89+
90+
static_assert(not simply::copy_constructible<movable>);
91+
static_assert(simply::move_constructible<movable>);
92+
static_assert(not std::is_destructible_v<movable>);
93+
94+
static_assert(constructible_from_in_place<destructible, semiregular>);
95+
static_assert(constructible_from_in_place<destructible, uncopyable>);
96+
static_assert(constructible_from_in_place<destructible, immovable>);
97+
static_assert(not constructible_from_in_place<destructible, indestructible>);
98+
99+
static_assert(not simply::copy_constructible<destructible>);
100+
static_assert(not simply::move_constructible<destructible>);
101+
static_assert(std::is_destructible_v<destructible>);

0 commit comments

Comments
 (0)