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+
1837template <typename Allocator = std::allocator<std::byte>>
1938struct 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
44107template <simply::specialization_of<simply::allocator_storage> Storage,
@@ -48,23 +111,6 @@ struct iface<Storage, Self> {
48111private:
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 ;
0 commit comments