Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2026 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #pragma once
23 :
24 : #include <cstddef>
25 : #include <functional>
26 : #include <new>
27 : #include <type_traits>
28 : #include <utility>
29 :
30 : namespace muse::functional {
31 : template<typename Signature, size_t Capacity = 32, size_t Alignment = alignof(std::max_align_t)>
32 : class MoveOnlyInplaceFunction;
33 :
34 : namespace detail {
35 : template<typename>
36 : struct IsMoveOnlyInplaceFunction : std::false_type {};
37 :
38 : template<typename Signature, size_t Capacity, size_t Alignment>
39 : struct IsMoveOnlyInplaceFunction<MoveOnlyInplaceFunction<Signature, Capacity, Alignment> > : std::true_type {};
40 :
41 : template<typename Ret, typename ... Args>
42 : struct MoveOnlyInplaceFunctionVTable
43 : {
44 : using Invoke = Ret (*)(void*, Args&&...);
45 : using Relocate = void (*)(void*, void*) noexcept;
46 : using Destroy = void (*)(void*) noexcept;
47 :
48 : Invoke invoke = nullptr;
49 : Relocate relocate = nullptr;
50 : Destroy destroy = nullptr;
51 : };
52 :
53 : template<typename Callable, typename Ret, typename ... Args>
54 : Ret invokeMoveOnlyInplaceFunction(void* storage, Args&&... args)
55 : {
56 : if constexpr (std::is_void_v<Ret>) {
57 : (*static_cast<Callable*>(storage))(std::forward<Args>(args)...);
58 : } else {
59 : return (*static_cast<Callable*>(storage))(std::forward<Args>(args)...);
60 : }
61 : }
62 :
63 : template<typename Callable>
64 : void relocateMoveOnlyInplaceFunction(void* destination, void* source) noexcept
65 : {
66 : auto* callable = static_cast<Callable*>(source);
67 : new (destination) Callable(std::move(*callable));
68 : callable->~Callable();
69 : }
70 :
71 : template<typename Callable>
72 : void destroyMoveOnlyInplaceFunction(void* storage) noexcept
73 : {
74 : static_cast<Callable*>(storage)->~Callable();
75 : }
76 :
77 : template<typename Callable, typename Ret, typename ... Args>
78 : const MoveOnlyInplaceFunctionVTable<Ret, Args...>* moveOnlyInplaceFunctionVTable() noexcept
79 : {
80 : static const MoveOnlyInplaceFunctionVTable<Ret, Args...> vtable {
81 : &invokeMoveOnlyInplaceFunction<Callable, Ret, Args...>,
82 : &relocateMoveOnlyInplaceFunction<Callable>,
83 : &destroyMoveOnlyInplaceFunction<Callable>
84 : };
85 :
86 : return &vtable;
87 : }
88 : } // namespace detail
89 :
90 : template<typename Ret, typename ... Args, size_t Capacity, size_t Alignment>
91 : class MoveOnlyInplaceFunction<Ret(Args...), Capacity, Alignment>
92 : {
93 : public:
94 48 : MoveOnlyInplaceFunction() noexcept = default;
95 :
96 : MoveOnlyInplaceFunction(std::nullptr_t) noexcept {}
97 :
98 : MoveOnlyInplaceFunction(const MoveOnlyInplaceFunction&) = delete;
99 : MoveOnlyInplaceFunction& operator=(const MoveOnlyInplaceFunction&) = delete;
100 :
101 : MoveOnlyInplaceFunction(MoveOnlyInplaceFunction&& other) noexcept
102 : {
103 : moveFrom(std::move(other));
104 : }
105 :
106 : MoveOnlyInplaceFunction& operator=(MoveOnlyInplaceFunction&& other) noexcept
107 : {
108 : if (this != &other) {
109 : reset();
110 : moveFrom(std::move(other));
111 : }
112 :
113 : return *this;
114 : }
115 :
116 : template<typename Callable,
117 : typename Fn = std::decay_t<Callable>,
118 : std::enable_if_t<!detail::IsMoveOnlyInplaceFunction<Fn>::value
119 : && std::is_invocable_r_v<Ret, Fn&, Args...>, int> = 0>
120 : MoveOnlyInplaceFunction(Callable&& callable)
121 : {
122 : static_assert(sizeof(Fn) <= Capacity, "Callable is too large for this MoveOnlyInplaceFunction");
123 : static_assert(alignof(Fn) <= Alignment, "Callable alignment is too large for this MoveOnlyInplaceFunction");
124 : static_assert(std::is_nothrow_move_constructible_v<Fn>,
125 : "MoveOnlyInplaceFunction requires nothrow move-constructible callables");
126 :
127 : m_vtable = detail::moveOnlyInplaceFunctionVTable<Fn, Ret, Args...>();
128 : new (storage()) Fn(std::forward<Callable>(callable));
129 : }
130 :
131 : template<typename Callable,
132 : typename Fn = std::decay_t<Callable>,
133 : std::enable_if_t<!detail::IsMoveOnlyInplaceFunction<Fn>::value
134 : && std::is_invocable_r_v<Ret, Fn&, Args...>, int> = 0>
135 : MoveOnlyInplaceFunction& operator=(Callable&& callable)
136 : {
137 : *this = MoveOnlyInplaceFunction(std::forward<Callable>(callable));
138 : return *this;
139 : }
140 :
141 46 : MoveOnlyInplaceFunction& operator=(std::nullptr_t) noexcept
142 : {
143 46 : reset();
144 45 : return *this;
145 : }
146 :
147 47 : ~MoveOnlyInplaceFunction() noexcept
148 : {
149 47 : reset();
150 47 : }
151 :
152 : Ret operator()(Args... args) const
153 : {
154 : if (!m_vtable) {
155 : throw std::bad_function_call();
156 : }
157 :
158 : if constexpr (std::is_void_v<Ret>) {
159 : m_vtable->invoke(storage(), std::forward<Args>(args)...);
160 : } else {
161 : return m_vtable->invoke(storage(), std::forward<Args>(args)...);
162 : }
163 : }
164 :
165 45 : explicit operator bool() const noexcept
166 : {
167 45 : return m_vtable != nullptr;
168 : }
169 :
170 91 : void reset() noexcept
171 : {
172 91 : if (!m_vtable) {
173 91 : return;
174 : }
175 :
176 0 : m_vtable->destroy(storage());
177 0 : m_vtable = nullptr;
178 : }
179 :
180 : private:
181 : using VTable = detail::MoveOnlyInplaceFunctionVTable<Ret, Args...>;
182 : using Storage = std::aligned_storage_t<Capacity, Alignment>;
183 :
184 0 : void* storage() const noexcept
185 : {
186 0 : return const_cast<Storage*>(&m_storage);
187 : }
188 :
189 : void moveFrom(MoveOnlyInplaceFunction&& other) noexcept
190 : {
191 : if (!other.m_vtable) {
192 : return;
193 : }
194 :
195 : m_vtable = other.m_vtable;
196 : m_vtable->relocate(storage(), other.storage());
197 : other.m_vtable = nullptr;
198 : }
199 :
200 : const VTable* m_vtable = nullptr;
201 : mutable Storage m_storage {};
202 : };
203 :
204 : template<typename Signature, size_t Capacity, size_t Alignment>
205 : bool operator==(const MoveOnlyInplaceFunction<Signature, Capacity, Alignment>& function, std::nullptr_t) noexcept
206 : {
207 : return !function;
208 : }
209 :
210 : template<typename Signature, size_t Capacity, size_t Alignment>
211 : bool operator!=(const MoveOnlyInplaceFunction<Signature, Capacity, Alignment>& function, std::nullptr_t) noexcept
212 : {
213 : return bool(function);
214 : }
215 : } // namespace muse::functional
|