1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef LIB_FIT_FUNCTION_H_
6 #define LIB_FIT_FUNCTION_H_
7
8 #include <memory>
9 #include <type_traits>
10
11 #include "function_internal.h"
12 #include "nullable.h"
13
14 namespace fit {
15
16 template <size_t inline_target_size, bool require_inline,
17 typename Result, typename... Args>
18 class function_impl;
19
20 // The default size allowance for storing a target inline within a function
21 // object, in bytes. This default allows for inline storage of targets
22 // as big as two pointers, such as an object pointer and a pointer to a member
23 // function.
24 constexpr size_t default_inline_target_size = sizeof(void*) * 2;
25
26 // A |fit::function| is a move-only polymorphic function wrapper.
27 //
28 // |fit::function<T>| behaves like |std::function<T>| except that it is move-only
29 // instead of copyable so it can hold targets which cannot be copied, such as
30 // mutable lambdas.
31 //
32 // Targets of up to |inline_target_size| bytes in size (rounded up for memory
33 // alignment) are stored inline within the function object without incurring
34 // any heap allocation. Larger callable objects will be moved to the heap as
35 // required.
36 //
37 // See also |fit::inline_function<T, size>| for more control over allocation
38 // behavior.
39 //
40 // SYNOPSIS
41 //
42 // |T| is the function's signature. e.g. void(int, std::string).
43 //
44 // |inline_target_size| is the minimum size of target that is guaranteed to
45 // fit within a function without requiring heap allocation.
46 // Defaults to |default_inline_target_size|.
47 //
48 // Class members are documented in |fit::function_impl|.
49 //
50 // EXAMPLES
51 //
52 // - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/function_example1.cpp
53 // - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/function_example2.cpp
54 //
55 template <typename T, size_t inline_target_size = default_inline_target_size>
56 using function = function_impl<inline_target_size, false, T>;
57
58 // A move-only callable object wrapper which forces callables to be stored inline
59 // and never performs heap allocation.
60 //
61 // Behaves just like |fit::function<T, inline_target_size>| except that attempting
62 // to store a target larger than |inline_target_size| will fail to compile.
63 template <typename T, size_t inline_target_size = default_inline_target_size>
64 using inline_function = function_impl<inline_target_size, true, T>;
65
66 // Synonym for a function which takes no arguments and produces no result.
67 using closure = function<void()>;
68
69 // Function implementation details.
70 // See |fit::function| documentation for more information.
71 template <size_t inline_target_size, bool require_inline,
72 typename Result, typename... Args>
73 class function_impl<inline_target_size, require_inline, Result(Args...)> final {
74 using ops_type = const ::fit::internal::target_ops<Result, Args...>*;
75 using storage_type = typename std::aligned_storage<
76 (inline_target_size >= sizeof(void*)
77 ? inline_target_size
78 : sizeof(void*))>::type; // avoid including <algorithm> just for max
79 template <typename Callable>
80 using target_type = ::fit::internal::target<
81 Callable,
82 (sizeof(Callable) <= sizeof(storage_type)),
83 Result, Args...>;
84 using null_target_type = target_type<decltype(nullptr)>;
85
86 public:
87 // The function's result type.
88 using result_type = Result;
89
90 // // Creates a function with an empty target.
function_impl()91 function_impl() {
92 initialize_null_target();
93 }
94
95 // Creates a function with an empty target.
function_impl(decltype (nullptr))96 function_impl(decltype(nullptr)) {
97 initialize_null_target();
98 }
99
100 // Creates a function bound to the specified function pointer.
101 // If target == nullptr, assigns an empty target.
function_impl(Result (* target)(Args...))102 function_impl(Result (*target)(Args...)) {
103 initialize_target(target);
104 }
105
106 // Creates a function bound to the specified callable object.
107 // If target == nullptr, assigns an empty target.
108 //
109 // For functors, we need to capture the raw type but also restrict on the existence of an
110 // appropriate operator () to resolve overloads and implicit casts properly.
111 template <typename Callable,
112 typename = std::enable_if_t<
113 std::is_convertible<
114 decltype(std::declval<Callable&>()(
115 std::declval<Args>()...)),
116 result_type>::value>>
function_impl(Callable target)117 function_impl(Callable target) {
118 initialize_target(std::move(target));
119 }
120
121 // Creates a function with a target moved from another function,
122 // leaving the other function with an empty target.
function_impl(function_impl && other)123 function_impl(function_impl&& other) {
124 move_target_from(std::move(other));
125 }
126
127 // Destroys the function, releasing its target.
~function_impl()128 ~function_impl() {
129 destroy_target();
130 }
131
132 // Returns true if the function has a non-empty target.
133 explicit operator bool() const {
134 return ops_ != &null_target_type::ops;
135 };
136
137 // Invokes the function's target.
138 // Aborts if the function's target is empty.
operator()139 Result operator()(Args... args) const {
140 return ops_->invoke(&bits_, std::forward<Args>(args)...);
141 }
142
143 // Assigns an empty target.
decltype(nullptr)144 function_impl& operator=(decltype(nullptr)) {
145 destroy_target();
146 initialize_null_target();
147 return *this;
148 }
149
150 // Assigns the function's target.
151 // If target == nullptr, assigns an empty target.
152 template <typename Callable,
153 typename = std::enable_if_t<
154 std::is_convertible<
155 decltype(std::declval<Callable&>()(
156 std::declval<Args>()...)),
157 result_type>::value>>
158 function_impl& operator=(Callable target) {
159 destroy_target();
160 initialize_target(std::move(target));
161 return *this;
162 }
163
164 // Assigns the function with a target moved from another function,
165 // leaving the other function with an empty target.
166 function_impl& operator=(function_impl&& other) {
167 if (&other == this)
168 return *this;
169 destroy_target();
170 move_target_from(std::move(other));
171 return *this;
172 }
173
174 // Swaps the functions' targets.
swap(function_impl & other)175 void swap(function_impl& other) {
176 if (&other == this)
177 return;
178 ops_type temp_ops = ops_;
179 storage_type temp_bits;
180 ops_->move(&bits_, &temp_bits);
181
182 ops_ = other.ops_;
183 other.ops_->move(&other.bits_, &bits_);
184
185 other.ops_ = temp_ops;
186 temp_ops->move(&temp_bits, &other.bits_);
187 }
188
189 // Returns a pointer to the function's target.
190 template <typename Callable>
target()191 Callable* target() {
192 check_target_type<Callable>();
193 return static_cast<Callable*>(ops_->get(&bits_));
194 }
195
196 // Returns a pointer to the function's target.
197 template <typename Callable>
target()198 const Callable* target() const {
199 check_target_type<Callable>();
200 return static_cast<Callable*>(ops_->get(&bits_));
201 }
202
203 // Returns a new function object which invokes the same target.
204 // The target itself is not copied; it is moved to the heap and its
205 // lifetime is extended until all references have been released.
206 //
207 // Note: This method is not supported on |fit::inline_function<>|
208 // because it may incur a heap allocation which is contrary to
209 // the stated purpose of |fit::inline_function<>|.
share()210 function_impl share() {
211 static_assert(!require_inline, "Inline functions cannot be shared.");
212 // TODO(jeffbrown): Replace shared_ptr with a better ref-count mechanism.
213 // TODO(jeffbrown): This definition breaks the client's ability to use
214 // |target()| because the target's type has changed. We could fix this
215 // by defining a new target type (and vtable) for shared targets
216 // although it would be nice to avoid memory overhead and code expansion
217 // when sharing is not used.
218 struct ref {
219 std::shared_ptr<function_impl> target;
220 Result operator()(Args... args) {
221 return (*target)(std::forward<Args>(args)...);
222 }
223 };
224 if (ops_ != &target_type<ref>::ops) {
225 if (ops_ == &null_target_type::ops) {
226 return nullptr;
227 }
228 auto target = ref{std::make_shared<function_impl>(std::move(*this))};
229 *this = std::move(target);
230 }
231 return function_impl(*static_cast<ref*>(ops_->get(&bits_)));
232 }
233
234 function_impl(const function_impl& other) = delete;
235 function_impl& operator=(const function_impl& other) = delete;
236
237 private:
238 // assumes target is uninitialized
initialize_null_target()239 void initialize_null_target() {
240 ops_ = &null_target_type::ops;
241 }
242
243 // assumes target is uninitialized
244 template <typename Callable>
initialize_target(Callable target)245 void initialize_target(Callable target) {
246 static_assert(!require_inline || sizeof(Callable) <= inline_target_size,
247 "Callable too large to store inline as requested.");
248 if (is_null(target)) {
249 initialize_null_target();
250 } else {
251 ops_ = &target_type<Callable>::ops;
252 target_type<Callable>::initialize(&bits_, std::move(target));
253 }
254 }
255
256 // leaves target uninitialized
destroy_target()257 void destroy_target() {
258 ops_->destroy(&bits_);
259 }
260
261 // leaves other target initialized to null
move_target_from(function_impl && other)262 void move_target_from(function_impl&& other) {
263 ops_ = other.ops_;
264 other.ops_->move(&other.bits_, &bits_);
265 other.initialize_null_target();
266 }
267
268 template <typename Callable>
check_target_type()269 void check_target_type() const {
270 if (ops_ != &target_type<Callable>::ops)
271 abort();
272 }
273
274 ops_type ops_;
275 mutable storage_type bits_;
276 }; // namespace fit
277
278 template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
swap(function_impl<inline_target_size,require_inline,Result,Args...> & a,function_impl<inline_target_size,require_inline,Result,Args...> & b)279 void swap(function_impl<inline_target_size, require_inline, Result, Args...>& a,
280 function_impl<inline_target_size, require_inline, Result, Args...>& b) {
281 a.swap(b);
282 }
283
284 template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
285 bool operator==(const function_impl<inline_target_size, require_inline, Result, Args...>& f,
286 decltype(nullptr)) {
287 return !f;
288 }
289 template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
290 bool operator==(decltype(nullptr),
291 const function_impl<inline_target_size, require_inline, Result, Args...>& f) {
292 return !f;
293 }
294 template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
295 bool operator!=(const function_impl<inline_target_size, require_inline, Result, Args...>& f,
296 decltype(nullptr)) {
297 return !!f;
298 }
299 template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
300 bool operator!=(decltype(nullptr),
301 const function_impl<inline_target_size, require_inline, Result, Args...>& f) {
302 return !!f;
303 }
304
305 // Returns a Callable object which invokes a member function of an object.
306 template <typename R, typename T, typename... Args>
bind_member(T * instance,R (T::* fn)(Args...))307 auto bind_member(T* instance, R (T::*fn)(Args...)) {
308 return [instance, fn](Args... args) {
309 return (instance->*fn)(std::forward<Args>(args)...);
310 };
311 }
312
313 } // namespace fit
314
315 #endif // LIB_FIT_FUNCTION_H_
316