13#include <initializer_list>
19namespace tcspc::internal {
21struct bad_move_only_any_cast : std::bad_cast {
22 [[nodiscard]]
auto what() const noexcept ->
char const *
override {
23 return "bad_move_only_any_cast";
28constexpr std::size_t move_only_any_sbo_size = 3 *
sizeof(
void *);
34 virtual ~polymorphic() =
default;
35 virtual void move_construct_at(std::byte *storage)
noexcept = 0;
36 [[nodiscard]]
virtual auto const_ptr() const noexcept
38 [[nodiscard]] virtual auto ptr() noexcept ->
void * = 0;
39 [[nodiscard]] virtual auto has_value() const noexcept ->
bool = 0;
40 [[nodiscard]] virtual auto type() const noexcept
41 -> std::type_info const & = 0;
44 struct polymorphic_no_value : public polymorphic {
45 void move_construct_at(std::byte *storage)
noexcept override {
46 new (storage) polymorphic_no_value();
49 [[nodiscard]]
auto const_ptr() const noexcept
50 ->
void const *
override {
54 [[nodiscard]]
auto ptr() noexcept ->
void *
override {
58 [[nodiscard]]
auto has_value() const noexcept ->
bool override {
62 [[nodiscard]]
auto type() const noexcept
63 -> std::type_info const &
override {
69 template <
typename V>
struct polymorphic_on_heap :
public polymorphic {
70 std::unique_ptr<V> value;
72 explicit polymorphic_on_heap(std::unique_ptr<V> v)
73 : value(std::move(v)) {}
75 void move_construct_at(std::byte *storage)
noexcept override {
76 new (storage) polymorphic_on_heap(std::move(value));
79 [[nodiscard]]
auto const_ptr() const noexcept
80 ->
void const *
override {
84 [[nodiscard]]
auto ptr() noexcept ->
void *
override {
88 [[nodiscard]]
auto has_value() const noexcept ->
bool override {
92 [[nodiscard]]
auto type() const noexcept
93 -> std::type_info const &
override {
99 template <
typename V>
struct polymorphic_sbo :
public polymorphic {
102 template <
typename... Args>
103 explicit polymorphic_sbo(Args &&...args)
104 : value(std::forward<Args>(args)...) {}
106 void move_construct_at(std::byte *storage)
noexcept override {
107 new (storage) polymorphic_sbo(std::move(value));
110 [[nodiscard]]
auto const_ptr() const noexcept
111 ->
void const *
override {
115 [[nodiscard]]
auto ptr() noexcept ->
void *
override {
return &value; }
117 [[nodiscard]]
auto has_value() const noexcept ->
bool override {
121 [[nodiscard]]
auto type() const noexcept
122 -> std::type_info const &
override {
127 static constexpr std::size_t storage_size = std::max(
128 sizeof(polymorphic_on_heap<int>),
130 polymorphic_sbo<std::array<std::byte, move_only_any_sbo_size>>));
132 static constexpr std::size_t storage_align =
133 alignof(polymorphic_on_heap<int>);
136 alignas(storage_align) std::byte storage[storage_size];
139 [[nodiscard]]
auto p_storage() noexcept -> std::
byte * {
140 return static_cast<std::byte *
>(storage);
143 [[nodiscard]]
auto poly() noexcept -> polymorphic * {
145 return reinterpret_cast<polymorphic *
>(storage);
148 [[nodiscard]]
auto poly() const noexcept -> polymorphic const * {
150 return reinterpret_cast<polymorphic
const *
>(storage);
153 template <
typename V>
static constexpr auto uses_sbo() noexcept ->
bool {
154 return sizeof(polymorphic_sbo<V>) <= storage_size &&
155 alignof(polymorphic_sbo<V>) <= storage_align;
158 template <
typename U>
159 friend auto move_only_any_cast(move_only_any
const &operand) -> U;
161 template <
typename U>
162 friend auto move_only_any_cast(move_only_any &&operand) -> U;
164 template <
typename V>
165 friend auto move_only_any_cast(move_only_any
const *operand)
noexcept
168 template <
typename V>
169 friend auto move_only_any_cast(move_only_any *operand)
noexcept -> V *;
173 move_only_any() {
new (p_storage()) polymorphic_no_value(); }
175 ~move_only_any() { poly()->~polymorphic(); }
177 move_only_any(move_only_any
const &other) =
delete;
178 auto operator=(move_only_any
const &rhs) =
delete;
181 move_only_any(move_only_any &&other)
noexcept {
182 other.poly()->move_construct_at(p_storage());
186 auto operator=(move_only_any &&rhs)
noexcept -> move_only_any & {
187 poly()->~polymorphic();
188 rhs.poly()->move_construct_at(p_storage());
193 template <
typename V>
194 requires(not std::same_as<std::remove_reference_t<V>, move_only_any>)
196 move_only_any(V &&value) {
197 using U = std::decay_t<V>;
198 if constexpr (uses_sbo<U>()) {
199 new (p_storage()) polymorphic_sbo<U>(std::forward<V>(value));
201 new (p_storage()) polymorphic_on_heap<U>(
202 std::make_unique<U>(std::forward<V>(value)));
206 template <
typename V>
auto operator=(V &&rhs) -> move_only_any & {
207 using U = std::decay_t<V>;
208 poly()->~polymorphic();
209 if constexpr (uses_sbo<U>()) {
210 new (p_storage()) polymorphic_sbo<U>(std::forward<V>(rhs));
212 new (p_storage()) polymorphic_on_heap<U>(
213 std::make_unique<U>(std::forward<V>(rhs)));
218 template <
typename V,
typename... Args>
220 explicit move_only_any(std::in_place_type_t<V> , Args &&...args) {
221 if constexpr (uses_sbo<V>()) {
222 new (p_storage()) polymorphic_sbo<V>(std::forward<Args>(args)...);
224 new (p_storage()) polymorphic_on_heap(
225 std::make_unique<V>(std::forward<Args>(args)...));
229 template <
typename V,
typename T,
typename... Args>
231 explicit move_only_any(std::in_place_type_t<V> ,
232 std::initializer_list<T> il, Args &&...args) {
233 if constexpr (uses_sbo<V>()) {
235 polymorphic_sbo<V>(il, std::forward<Args>(args)...);
237 new (p_storage()) polymorphic_on_heap(
238 std::make_unique<V>(il, std::forward<Args>(args)...));
242 template <
typename V,
typename... Args>
243 auto emplace(Args &&...args) -> std::decay_t<V> & {
244 using U = std::decay_t<V>;
245 poly()->~polymorphic();
246 if constexpr (uses_sbo<U>()) {
247 new (p_storage()) polymorphic_sbo<U>(std::forward<Args>(args)...);
248 return *
static_cast<U *
>(
250 reinterpret_cast<polymorphic_sbo<U> *
>(storage)->ptr());
252 new (p_storage()) polymorphic_on_heap<U>(
253 std::make_unique<U>(std::forward<Args>(args)...));
254 return *
static_cast<U *
>(
256 reinterpret_cast<polymorphic_on_heap<U> *
>(storage)->ptr());
260 template <
typename V,
typename T,
typename... Args>
261 auto emplace(std::initializer_list<T> il, Args &&...args)
262 -> std::decay_t<V> & {
263 using U = std::decay_t<V>;
264 poly()->~polymorphic();
265 if constexpr (uses_sbo<U>()) {
267 polymorphic_sbo<U>(il, std::forward<Args>(args)...);
268 return *
static_cast<U *
>(
270 reinterpret_cast<polymorphic_sbo<U> *
>(storage)->ptr());
272 new (p_storage()) polymorphic_on_heap<U>(
273 std::make_unique<U>(il, std::forward<Args>(args)...));
274 return *
static_cast<U *
>(
276 reinterpret_cast<polymorphic_on_heap<U> *
>(storage)->ptr());
280 void reset() noexcept {
281 poly()->~polymorphic();
282 new (p_storage()) polymorphic_no_value();
285 void swap(move_only_any &other)
noexcept {
290 [[nodiscard]]
auto has_value() const noexcept ->
bool {
294 return poly()->has_value();
297 [[nodiscard]]
auto type() const noexcept -> std::type_info const & {
298 return poly()->type();
304auto move_only_any_cast(move_only_any
const &operand) -> U {
305 using V = std::remove_cv_t<std::remove_reference_t<U>>;
306 auto const *ptr = move_only_any_cast<V>(&operand);
308 throw bad_move_only_any_cast();
313template <
typename U>
auto move_only_any_cast(move_only_any &operand) -> U {
314 using V = std::remove_cv_t<std::remove_reference_t<U>>;
315 auto *ptr = move_only_any_cast<V>(&operand);
317 throw bad_move_only_any_cast();
323template <
typename U>
auto move_only_any_cast(move_only_any &&operand) -> U {
324 using V = std::remove_cv_t<std::remove_reference_t<U>>;
325 auto *ptr = move_only_any_cast<V>(&operand);
327 throw bad_move_only_any_cast();
328 return std::move(*ptr);
333auto move_only_any_cast(move_only_any
const *operand)
noexcept -> V
const * {
334 static_assert(not std::is_void_v<V>);
335 if (operand->type() !=
typeid(V))
337 return static_cast<V
const *
>(operand->poly()->const_ptr());
342auto move_only_any_cast(move_only_any *operand)
noexcept -> V * {
343 static_assert(not std::is_void_v<V>);
344 if (operand->type() !=
typeid(V))
346 return static_cast<V *
>(operand->poly()->ptr());
349template <
typename V,
typename... Args>
350auto make_move_only_any(Args &&...args) -> move_only_any {
351 static_assert(not std::is_void_v<V>);
352 return move_only_any(std::in_place_type<V>, std::forward<Args>(args)...);
355template <
typename V,
typename T,
typename... Args>
356auto make_move_only_any(std::initializer_list<T> il, Args &&...args)
358 static_assert(not std::is_void_v<V>);
359 return move_only_any(std::in_place_type<V>, il,
360 std::forward<Args>(args)...);