"ScL Feature"/
ScL Feature 0.0.1
C++20 composable proxy wrapper with pluggable executor strategy
Loading...
Searching...
No Matches
property_execution.h
1#pragma once
2
44
46#include <scl/utility/type_traits/forward_like.h>
47
48#include <cstddef>
49#include <type_traits>
50#include <utility>
51
52namespace scl::feature::detail
53{
61 template <typename OuterExecutorType, typename PropertyTag>
62 struct property_execution
63 {
69 template <typename PropertyType>
70 class holder
71 {
72 public:
73 using value_type = PropertyType;
74
77 using outer_value_type = ::std::remove_reference_t<
78 decltype(OuterExecutorType::access(::std::declval<OuterExecutorType &>()))>;
79
82 using member_pointer_type = PropertyType outer_value_type::*;
83
84 private:
85 ::std::ptrdiff_t m_offset;
86 member_pointer_type m_property_pointer;
87
88 private:
89 // ── offset-based executor access ──────────────────────────────────
90
103 template <typename Self>
104 [[nodiscard]]
105 static auto
106 outer_exec_ptr(Self && self) noexcept // NOLINT(cppcoreguidelines-missing-std-forward)
107 requires ::std::same_as<::std::remove_cvref_t<Self>, holder>
108 {
109 using exec_obj = ::std::remove_reference_t<::scl::forward_like_t<Self, OuterExecutorType>>;
110 using byte_cv = ::std::remove_reference_t<::scl::forward_like_t<Self, ::std::byte>>;
111
112 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
113 byte_cv * const cursor = reinterpret_cast<byte_cv *>(&self) - self.m_offset;
114 return reinterpret_cast<exec_obj *>(cursor); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
115 }
116
117 // ── noexcept predicate for access() ────────────────────────────────
118
124 template <typename Self>
125 static constexpr bool access_noexcept =
126 PropertyTag::template override_noexcept<::scl::forward_like_t<Self, OuterExecutorType>>;
127
128 public:
129 // ── construction ──────────────────────────────────────────────────
130
133 holder(OuterExecutorType const * exec, member_pointer_type property_pointer) noexcept
134 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
135 : m_offset{reinterpret_cast<::std::byte const *>(this) - reinterpret_cast<::std::byte const *>(exec)}
136 , m_property_pointer{property_pointer}
137 {}
138
144 holder(holder const &) = default;
145 holder(holder &&) = default;
146 holder & operator=(holder const &) = delete;
147 holder & operator=(holder &&) = delete;
148 ~holder() = default;
149
150 // ── executor interface ────────────────────────────────────────────
151
166 template <typename Self>
167 static decltype(auto) access(Self && self) noexcept(access_noexcept<Self>)
168 requires ::std::same_as<::std::remove_cvref_t<Self>, holder>
169 {
170 using exec_refer = ::scl::forward_like_t<Self, OuterExecutorType>;
171
172 if constexpr (PropertyTag::template has_override<exec_refer>)
173 {
174 return PropertyTag::call_override(
175 ::scl::forward_like<Self>(*outer_exec_ptr(::std::forward<Self>(self))));
176 }
177 else
178 {
179 auto property_pointer = self.m_property_pointer;
180 auto * const exec = outer_exec_ptr(::std::forward<Self>(self));
181 return OuterExecutorType::execute(::scl::forward_like<Self>(*exec),
182 [property_pointer](auto && scl_e) -> decltype(auto) {
183 // access() is resolved here, inside execute() (after any guard()),
184 // so guarding executors (e.g. copy-on-write) project the field
185 // from the post-guard value rather than a pre-guard reference.
186 decltype(auto) v = OuterExecutorType::access(::std::forward<decltype(scl_e)>(scl_e));
187 using property_refer = ::scl::forward_like_t<decltype(v), PropertyType>;
188 return static_cast<property_refer>(::std::forward<decltype(v)>(v).*property_pointer);
189 }, ::scl::forward_like<Self>(*exec));
190 }
191 }
192
203 template <typename Self, typename Func>
204 static decltype(auto) execute(Self && self, Func && func)
205 requires ::std::same_as<::std::remove_cvref_t<Self>, holder>
206 {
207 return OuterExecutorType::execute(
208 ::scl::forward_like<Self>(*outer_exec_ptr(::std::forward<Self>(self))),
209 ::std::forward<Func>(func));
210 }
211
212 // ── optional guard / unguard ──────────────────────────────────────
213
216 template <typename Self>
217 static void guard(Self && self) /**/
219 requires ::std::same_as<::std::remove_cvref_t<Self>, holder> &&
220 requires {
221 OuterExecutorType::guard(
222 ::std::declval<::scl::forward_like_t<Self, OuterExecutorType>>());
223 }
224 {
225 OuterExecutorType::guard(
226 ::scl::forward_like<Self>(*outer_exec_ptr(::std::forward<Self>(self))));
227 }
228
231 template <typename Self>
232 static void unguard(Self && self) /**/
234 requires ::std::same_as<::std::remove_cvref_t<Self>, holder> &&
235 requires {
236 OuterExecutorType::unguard(
237 ::std::declval<::scl::forward_like_t<Self, OuterExecutorType>>());
238 }
239 {
240 OuterExecutorType::unguard(
241 ::scl::forward_like<Self>(*outer_exec_ptr(::std::forward<Self>(self))));
242 }
243 };
244 };
245} // namespace scl::feature::detail
constexpr bool is_unguard_noexcept_v
true if ExecutorType::unguard() for ExecutorRefer either does not exist or is noexcept.
Definition executor.h:194
constexpr bool is_guard_noexcept_v
true if ExecutorType::guard() for ExecutorRefer either does not exist or is noexcept.
Definition executor.h:182
Executor type traits: interface detection and noexcept helpers.