Skip to content

Commit d81eba4

Browse files
Bug fix in allocator_storage, refactor dispatch
1 parent 4d20a7e commit d81eba4

File tree

4 files changed

+132
-76
lines changed

4 files changed

+132
-76
lines changed

include/simply/dyn.hpp

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include <simply/concepts.hpp>
55
#include <simply/dispatch.hpp>
6-
#include <simply/elide.hpp>
76
#include <simply/iface.hpp>
87
#include <simply/impl.hpp>
98
#include <simply/storage.hpp>
@@ -106,6 +105,7 @@ struct affordance_type<simply::dyn<Affordance, Storage, Dispatch>> {
106105
using type = Affordance;
107106
};
108107

108+
// dynamic dispatch for member affordances of dyn
109109
template <simply::member_affordance Affordance,
110110
simply::specialization_of<simply::dyn> Dyn, typename R, typename Self,
111111
typename... Args, bool NoExcept>
@@ -116,17 +116,19 @@ struct impl<Affordance, Dyn, R(Self, Args...) noexcept(NoExcept)> {
116116
}
117117
};
118118

119-
// TODO refactor impl specializations below to not assume allocator_storage
119+
// TODO refactor specializations below to not assume allocator_storage
120120
template <typename Dyn>
121121
concept _allocator_storage_dyn =
122122
simply::specialization_of<Dyn, simply::dyn> and
123123
simply::specialization_of<typename Dyn::storage_type,
124124
simply::allocator_storage>;
125125

126-
// TODO resolve storage type from choices by checking each compatibility with T
127-
template <typename T, typename Dyn>
128-
inline constexpr const auto &_storage_fn =
129-
simply::fn<typename Dyn::storage_type, T>;
126+
// only copy the storage subobject of Dyn
127+
template <simply::_allocator_storage_dyn Dyn>
128+
struct affordance_traits<simply::copyable, Dyn> {
129+
using function_type =
130+
simply::iface<typename Dyn::storage_type, Dyn>(const Dyn &);
131+
};
130132

131133
template <simply::member_affordance Affordance, typename T,
132134
simply::_allocator_storage_dyn Dyn, typename R, typename Self,
@@ -136,48 +138,28 @@ struct impl<simply::impl<Affordance, T>, Dyn,
136138
static constexpr auto fn(Self dyn, Args... args) noexcept(NoExcept) -> R {
137139
using self_type = simply::apply_cvref_t<Self, T>;
138140

139-
const auto pointer = simply::_storage_fn<T, Dyn>(dyn.get());
141+
const auto pointer = simply::_dispatch_fn<T, Dyn>(dyn);
140142
return simply::fn<Affordance, T>(std::forward<self_type>(*pointer),
141143
std::forward<Args>(args)...);
142144
}
143145
};
144146

145-
template <simply::constructor_affordance Affordance, typename T,
146-
simply::_allocator_storage_dyn Dyn, typename R, typename Self,
147-
typename... Args, bool NoExcept>
148-
struct impl<simply::impl<Affordance, T>, Dyn,
149-
R(Self, Args...) noexcept(NoExcept)> {
150-
static_assert(std::same_as<R, Dyn>);
151-
152-
static constexpr auto fn(Self dyn, Args... args) -> Dyn {
153-
using self_type = simply::apply_cvref_t<Self, T>;
154-
return Dyn{
155-
std::allocator_arg,
156-
dyn.get_allocator(),
157-
std::in_place_type<T>,
158-
simply::elide([&] -> T {
159-
const auto pointer = simply::_storage_fn<T, Dyn>(dyn.get());
160-
return simply::fn<Affordance, T>(std::forward<self_type>(*pointer),
161-
std::forward<Args>(args)...);
162-
}),
163-
};
164-
}
165-
};
166-
147+
// TODO should this be specialized for simply::destructible only?
167148
template <simply::fundamental_destroy_affordance Affordance, typename T,
168149
simply::_allocator_storage_dyn Dyn, typename R, typename Self,
169150
typename... Args, bool NoExcept>
170151
struct impl<simply::impl<Affordance, T>, Dyn,
171152
R(Self, Args...) noexcept(NoExcept)> {
172-
static_assert(std::same_as<R(Self, Args...), void(Dyn &)> and NoExcept);
153+
static_assert(
154+
std::same_as<R(Self, Args...) noexcept(NoExcept), void(Dyn &) noexcept>);
173155

174156
static constexpr void fn(Dyn &dyn) noexcept {
175157
if (dyn.valueless_after_move()) {
176158
return;
177159
}
178160

179161
auto alloc = simply::_rebind_alloc<T>(dyn.get_allocator());
180-
const auto pointer = simply::_storage_fn<T, Dyn>(dyn.get());
162+
const auto pointer = simply::_dispatch_fn<T, Dyn>(dyn);
181163

182164
using traits = std::allocator_traits<decltype(alloc)>;
183165

include/simply/storage.hpp

Lines changed: 97 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef SIMPLY_STORAGE_HPP
22
#define SIMPLY_STORAGE_HPP
33

4+
#include <simply/copyable.hpp>
5+
#include <simply/elide.hpp>
46
#include <simply/iface.hpp>
57
#include <simply/impl.hpp>
68
#include <simply/scope.hpp>
@@ -15,30 +17,91 @@ template <typename T, typename Alloc>
1517
return allocator_type(alloc);
1618
}
1719

20+
template <typename Rebound, typename... Args>
21+
[[nodiscard]] static constexpr auto _construct_with(Rebound rebound,
22+
Args &&...args) {
23+
using rebound_traits = std::allocator_traits<Rebound>;
24+
auto pointer = rebound_traits::allocate(rebound, 1);
25+
// defer deallocation if construct throws
26+
auto dealloc = simply::scope_exit{
27+
[&] { rebound_traits::deallocate(rebound, pointer, 1); },
28+
};
29+
30+
rebound_traits::construct(rebound, std::to_address(pointer),
31+
std::forward<Args>(args)...);
32+
// cancel deallocation if construct returns
33+
dealloc.release();
34+
return pointer;
35+
}
36+
1837
template <typename Allocator = std::allocator<std::byte>>
1938
struct allocator_storage : simply::storage_affordance_base {
2039
using traits = std::allocator_traits<Allocator>;
2140
using void_pointer = traits::void_pointer;
2241
using const_void_pointer = traits::const_void_pointer;
2342

24-
// TODO express this by dispatching directly from iface to reduce boilerplate
2543
template <typename T>
26-
struct fn_t {
44+
struct dispatch : simply::member_affordance_base {
2745
using rebound = traits::template rebind_traits<T>;
2846
using pointer = rebound::pointer;
2947
using const_pointer = rebound::const_pointer;
3048

31-
static constexpr auto operator()(void_pointer ptr) noexcept {
32-
return static_cast<pointer>(ptr);
33-
}
49+
template <typename Self>
50+
struct fn_t {
51+
static constexpr auto operator()(Self &self) noexcept {
52+
return static_cast<pointer>(self.get());
53+
}
3454

35-
static constexpr auto operator()(const_void_pointer ptr) noexcept {
36-
return static_cast<const_pointer>(ptr);
37-
}
55+
static constexpr auto operator()(const Self &self) noexcept {
56+
return static_cast<const_pointer>(self.get());
57+
}
58+
};
59+
60+
template <typename Self>
61+
static constexpr fn_t<Self> fn{};
3862
};
63+
};
3964

40-
template <typename T>
41-
static constexpr fn_t<T> fn{};
65+
// TODO resolve storage type from choices by checking each compatibility with T
66+
template <typename T, typename Dyn>
67+
inline constexpr const auto &_dispatch_fn =
68+
simply::fn<typename Dyn::storage_type::template dispatch<T>>;
69+
70+
template <simply::fundamental_copy_affordance Copy, typename T,
71+
simply::specialization_of<simply::allocator_storage> Storage,
72+
typename Dyn, typename R, typename Self, typename... Args,
73+
bool NoExcept>
74+
struct impl<simply::impl<Copy, T>, simply::iface<Storage, Dyn>,
75+
R(Self, Args...) noexcept(NoExcept)> {
76+
using iface_type = simply::iface<Storage, Dyn>;
77+
static_assert(std::same_as<R(Self, Args...) noexcept(NoExcept),
78+
iface_type(const iface_type &)>);
79+
80+
// static dispatch for copy affordance of dyn with allocator storage
81+
static constexpr auto fn(Self self) -> R {
82+
using type = typename simply::iface<Storage, Dyn>::allocator_type;
83+
using traits = std::allocator_traits<type>;
84+
85+
const auto alloc =
86+
traits::select_on_container_copy_construction(self.get_allocator());
87+
88+
if (self.valueless_after_move()) {
89+
return R{std::allocator_arg, alloc, impl{}};
90+
}
91+
92+
return R{
93+
std::allocator_arg,
94+
alloc,
95+
std::in_place_type<T>,
96+
simply::elide([&] -> T {
97+
const auto pointer = simply::_dispatch_fn<T, Dyn>(self);
98+
return simply::fn<Copy, T>(*pointer);
99+
}),
100+
};
101+
}
102+
103+
private:
104+
impl() noexcept = default;
42105
};
43106

44107
template <simply::specialization_of<simply::allocator_storage> Storage,
@@ -48,23 +111,6 @@ struct iface<Storage, Self> {
48111
private:
49112
using traits = Storage::traits;
50113

51-
template <typename Rebound, typename... Args>
52-
[[nodiscard]] static constexpr auto _construct_with(Rebound rebound,
53-
Args &&...args) {
54-
using rebound_traits = std::allocator_traits<Rebound>;
55-
auto pointer = rebound_traits::allocate(rebound, 1);
56-
// defer deallocation if construct throws
57-
auto dealloc = simply::scope_exit{
58-
[&] { rebound_traits::deallocate(rebound, pointer, 1); },
59-
};
60-
61-
rebound_traits::construct(rebound, std::to_address(pointer),
62-
std::forward<Args>(args)...);
63-
// cancel deallocation if construct returns
64-
dealloc.release();
65-
return pointer;
66-
}
67-
68114
[[nodiscard]] constexpr auto _release() noexcept {
69115
return std::exchange(object_ptr, nullptr);
70116
}
@@ -74,16 +120,24 @@ struct iface<Storage, Self> {
74120
using void_pointer = traits::void_pointer;
75121
using const_void_pointer = traits::const_void_pointer;
76122

77-
// BUG use this->alloc instead of other.alloc to copy other->object_ptr
78-
constexpr iface(const iface &other)
79-
: alloc(traits::select_on_container_copy_construction(other.alloc)),
80-
object_ptr(
81-
other.valueless_after_move()
82-
? nullptr
83-
: simply::fn<
84-
simply::copy_affordance_t<typename Self::affordance_type>,
85-
Self>(static_cast<const Self &>(other))
86-
._release()) {}
123+
// effectively private "valueless" constructor
124+
template <typename Alloc, simply::fundamental_copy_affordance Copy,
125+
typename T>
126+
constexpr iface(std::allocator_arg_t alloc_tag, const Alloc &alloc,
127+
simply::impl<simply::impl<Copy, T>, iface> tag) noexcept
128+
requires std::default_initializable<void_pointer>
129+
: alloc(alloc), object_ptr() {}
130+
131+
constexpr iface(const iface &other) {
132+
using copy = simply::copy_affordance_t<typename Self::affordance_type>;
133+
134+
std::construct_at(this, simply::elide([&] {
135+
// TODO template storage on dispatch to access
136+
// get_member() without downcasting
137+
return simply::fn<copy>(
138+
static_cast<const Self &>(other));
139+
}));
140+
}
87141

88142
constexpr iface(iface &&other) noexcept
89143
: alloc(other.alloc), object_ptr(other._release()) {}
@@ -93,16 +147,17 @@ struct iface<Storage, Self> {
93147
const Alloc &alloc,
94148
[[maybe_unused]] std::in_place_type_t<T> obj_tag,
95149
Args &&...args)
96-
: alloc(alloc),
97-
object_ptr(_construct_with(simply::_rebind_alloc<T>(this->alloc),
98-
std::forward<Args>(args)...)) {}
150+
: alloc(alloc), object_ptr(simply::_construct_with(
151+
simply::_rebind_alloc<T>(this->alloc),
152+
std::forward<Args>(args)...)) {}
99153

100154
template <typename T, typename... Args>
101155
constexpr explicit iface([[maybe_unused]] std::in_place_type_t<T> tag,
102156
Args &&...args)
103157
requires std::default_initializable<allocator_type>
104-
: alloc(), object_ptr(_construct_with(simply::_rebind_alloc<T>(alloc),
105-
std::forward<Args>(args)...)) {}
158+
: alloc(),
159+
object_ptr(simply::_construct_with(simply::_rebind_alloc<T>(alloc),
160+
std::forward<Args>(args)...)) {}
106161

107162
auto operator=(const iface &other) -> iface & = default;
108163
auto operator=(iface &&other) noexcept -> iface & = default;

include/simply/type_traits.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ struct dispatch_affordance_base : simply::affordance_base {};
2727
template <typename Affordance, typename T>
2828
struct affordance_traits;
2929

30+
template <typename Affordance, typename T>
31+
using function_type_t = simply::affordance_traits<Affordance, T>::function_type;
32+
3033
template <typename T>
3134
struct affordance_type;
3235

3336
template <typename T>
34-
using affordance_type_t = affordance_type<T>::type;
37+
using affordance_type_t = simply::affordance_type<T>::type;
3538

3639
template <typename Affordance, typename T,
37-
typename Fn = affordance_traits<Affordance, T>::function_type>
40+
typename Fn = simply::function_type_t<Affordance, T>>
3841
struct impl;
3942

4043
template <typename Affordance, typename Tag>

include/simply/vtable.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef SIMPLY_VTABLE_HPP
22
#define SIMPLY_VTABLE_HPP
33

4+
#include <simply/iface.hpp>
45
#include <simply/impl.hpp>
56

67
namespace simply {
@@ -19,7 +20,14 @@ struct vtable<Affordance, Self> {};
1920

2021
template <simply::member_affordance Affordance, typename Self>
2122
struct vtable<Affordance, Self> {
22-
simply::affordance_traits<Affordance, Self>::function_type *fn;
23+
simply::function_type_t<Affordance, Self> *fn;
24+
};
25+
26+
// TODO specialize affordance_traits so this is unnecessary
27+
template <simply::constructor_affordance Affordance, typename Self>
28+
struct vtable<Affordance, Self> {
29+
using iface_type = simply::iface<typename Self::storage_type, Self>;
30+
simply::function_type_t<Affordance, iface_type> *fn;
2331
};
2432

2533
template <simply::affordance Affordance, typename Self, typename T>
@@ -45,6 +53,14 @@ inline constexpr simply::vtable<Affordance, Self>
4553
.fn = &simply::fn<simply::impl<Affordance, T>, Self>,
4654
};
4755

56+
// TODO specialize impl so this is unnecessary
57+
template <simply::constructor_affordance Affordance, typename Self, typename T>
58+
inline constexpr simply::vtable<Affordance, Self>
59+
vtable_for<Affordance, Self, T> = {
60+
.fn = &simply::fn<simply::impl<Affordance, T>,
61+
simply::iface<typename Self::storage_type, Self>>,
62+
};
63+
4864
} // namespace simply
4965

5066
#endif // SIMPLY_VTABLE_HPP

0 commit comments

Comments
 (0)