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