LeviLamina
Loading...
Searching...
No Matches
AnyFunction.h
1#pragma once
2
3#include <any>
4#include <array>
5#include <concepts>
6#include <functional>
7#include <span>
8
9#include "ll/api/base/Macro.h"
10#include "ll/api/base/TypeTraits.h"
11#include "ll/api/data/AnyObjBase.h"
12
13namespace ll::data {
15protected:
16 template <class Arg>
17 static auto getArg(std::any& any
18 ) -> std::conditional_t<std::is_reference_v<Arg>, Arg, std::add_lvalue_reference_t<Arg>> {
19 using Ret = std::conditional_t<std::is_reference_v<Arg>, Arg, std::add_lvalue_reference_t<Arg>>;
20 using Decay = std::decay_t<Arg>;
21 using Wrap = std::remove_cvref_t<Arg>;
22 if constexpr (std::is_const_v<std::remove_reference_t<Arg>>) {
23 if (auto* res = std::any_cast<std::reference_wrapper<std::add_const_t<Wrap>>>(&any)) {
24 return static_cast<Ret>(res->get());
25 }
26 }
27 if (auto* res = std::any_cast<std::reference_wrapper<Wrap>>(&any)) {
28 return static_cast<Ret>(res->get());
29 }
30 return static_cast<Ret>(std::any_cast<std::add_lvalue_reference_t<std::decay_t<Arg>>>(any));
31 }
32
33public:
34 static constexpr inline size_t anyFnSizeNumPtrs = 8;
35 static constexpr inline size_t smallObjSize = ((anyFnSizeNumPtrs - 1) * sizeof(void*));
36
37 virtual AnyFunctionObjBase* copy(void* to) const = 0;
38 virtual AnyFunctionObjBase* move(void* to) noexcept = 0;
39 virtual void tidy() noexcept = 0;
40 virtual std::any invoke(std::span<std::any>) = 0;
41};
42template <class Fn, class Ret, class... Args>
44 Fn fn;
45
46 template <std::size_t... I>
47 std::any invokeImpl(std::span<std::any> args, std::index_sequence<I...>) {
48 if constexpr (std::is_void_v<Ret>) {
49 (void)std::invoke(std::forward<decltype(fn)>(fn), getArg<Args>(args[I])...);
50 return {};
51 } else {
52 return std::make_any<Ret>(std::invoke(std::forward<decltype(fn)>(fn), getArg<Args>(args[I])...));
53 }
54 }
55
56public:
57 static constexpr inline bool nothrowMove = std::is_nothrow_move_constructible_v<Fn>;
58
59 template <class T>
60 requires(!std::is_same_v<T, AnyFunctionObj>)
61 constexpr explicit AnyFunctionObj(T&& fn) : fn(std::forward<T>(fn)) {}
62
63 std::any invoke(std::span<std::any> args) override { return invokeImpl(args, std::index_sequence_for<Args...>{}); }
64
65 template <class F>
66 static constexpr AnyFunctionObjBase* construct(void* to, F&& f) {
67 return constructImpl<AnyFunctionObj>(to, std::forward<F>(f));
68 }
69
70 AnyFunctionObjBase* copy(void* to) const override { return constructImpl<AnyFunctionObj>(to, fn); }
71 AnyFunctionObjBase* move(void* to) noexcept override { return constructImpl<AnyFunctionObj>(to, std::move(fn)); }
72 void tidy() noexcept override { destroyImpl<AnyFunctionObj>(this); }
73};
75 AnyFunctionObjBase* dataPtr{};
76 union {
77 std::max_align_t dummy;
78 char soo[AnyFunctionObjBase::smallObjSize];
79 };
80
81 constexpr bool isLarge() const noexcept { return dataPtr != static_cast<void const*>(&soo); }
82
83 constexpr bool hasValue() const noexcept { return dataPtr; }
84
85 void tidy() noexcept {
86 if (hasValue()) {
87 std::exchange(dataPtr, nullptr)->tidy();
88 }
89 }
90 void copy(AnyFunction const& other) {
91 if (other.hasValue()) {
92 dataPtr = other.dataPtr->copy(&soo);
93 }
94 }
95 void move(AnyFunction&& other) noexcept {
96 if (other.hasValue()) {
97 if (other.isLarge()) {
98 dataPtr = std::exchange(other.dataPtr, nullptr);
99 } else {
100 dataPtr = other.dataPtr->move(&soo);
101 other.tidy();
102 }
103 }
104 }
105
106public:
107 AnyFunction() = default;
108 AnyFunction(std::nullptr_t) {}
109 ~AnyFunction() { tidy(); }
110
111 AnyFunction(AnyFunction const& other) { copy(other); }
112 AnyFunction(AnyFunction&& other) noexcept { move(std::move(other)); }
113
114 AnyFunction& operator=(AnyFunction const& other) {
115 if (this != std::addressof(other)) {
116 tidy();
117 copy(other);
118 }
119 return *this;
120 }
121 AnyFunction& operator=(AnyFunction&& other) noexcept {
122 if (this != std::addressof(other)) {
123 tidy();
124 move(std::move(other));
125 }
126 return *this;
127 }
128 void swap(AnyFunction& other) noexcept {
129 if (isLarge() && other.isLarge()) {
130 std::swap(dataPtr, other.dataPtr);
131 } else {
132 AnyFunction temp;
133 temp.move(std::move(*this));
134 move(std::move(other));
135 other.move(std::move(temp));
136 }
137 }
138
139 template <class Fn, class Ret, class... Args>
140 requires(std::invocable<Fn, Args...>)
141 AnyFunction(std::in_place_type_t<Ret(Args...)>, Fn&& fn) {
142 if constexpr (std::is_pointer_v<Fn> || traits::is_specialization_of_v<Fn, std::function>
143 || std::is_member_pointer_v<Fn>) {
144 if (!fn) {
145 return;
146 }
147 }
148 dataPtr = AnyFunctionObj<std::decay_t<Fn>, Ret, Args...>::construct(&soo, std::forward<Fn>(fn));
149 }
150
151 template <class Fn>
152 AnyFunction(Fn&& fn)
153 : AnyFunction(std::in_place_type<typename traits::function_traits<Fn>::function_type>, std::forward<Fn>(fn)) {}
154
155 std::any invoke(std::span<std::any> args) const { return dataPtr->invoke(args); }
156
157 template <class... Args>
158 std::any operator()(Args&&... args) const {
159 auto arr =
160 std::array<std::any, sizeof...(Args)>{std::make_any<std::decay_t<Args>>(std::forward<Args>(args))...};
161 return invoke(arr);
162 }
163 constexpr explicit operator bool() const noexcept { return hasValue(); }
164};
165[[nodiscard]] inline bool operator==(AnyFunction const& other, std::nullptr_t) noexcept { return !other; }
166
167inline void swap(AnyFunction& l, AnyFunction& r) noexcept { l.swap(r); }
168
169} // namespace ll::data
Definition AnyFunction.h:14
Definition AnyFunction.h:43
Definition AnyFunction.h:74
Definition AnyObjBase.h:7
Definition TypeTraits.h:53