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 #pragma once
6 
7 #include <new>
8 #include <stddef.h>
9 #include <utility>
10 
11 #include <zircon/assert.h>
12 #include <fbl/algorithm.h>
13 #include <fbl/alloc_checker.h>
14 #include <fbl/macros.h>
15 #include <fbl/type_support.h>
16 #include <fbl/unique_ptr.h>
17 
18 namespace fbl {
19 namespace internal {
20 
21 // Checks if |T| is null. Defaults to false. |Comparison| is the type yielded by
22 // comparing a T value with nullptr.
23 template <typename T, typename Comparison = bool>
24 struct NullEq {
TestNullEq25     static constexpr bool Test(const T&) { return false; }
26 };
27 
28 // Partial specialization for |T| values comparable to nullptr.
29 template <typename T>
30 struct NullEq<T, decltype(*static_cast<T*>(nullptr) == nullptr)> {
31     // This is intended for a T that's a function pointer type.  However, it
32     // also matches for a T that can be implicitly coerced to a function
33     // pointer type, such as a function type or a captureless lambda's closure
34     // type.  In that case, the compiler might complain that the comparison is
35     // always false because the address of a function can never be a null
36     // pointer.  It's possible to do template selection to match function
37     // types, but it's not possible to match captureless lambda closure types
38     // that way.  So just suppress the warning.  The compiler will optimize
39     // away the always-false comparison.
40 #pragma GCC diagnostic push
41 #pragma GCC diagnostic ignored "-Waddress"
42     static constexpr bool Test(const T& v) { return v == nullptr; }
43 #pragma GCC diagnostic pop
44 };
45 
46 template <typename T>
47 static constexpr bool IsNull(const T& v) {
48     return NullEq<T>::Test(v);
49 }
50 
51 template <typename Result, typename... Args>
52 class FunctionTarget {
53 public:
54     FunctionTarget() = default;
55     virtual ~FunctionTarget() = default;
56 
57     DISALLOW_COPY_ASSIGN_AND_MOVE(FunctionTarget);
58 
59     virtual bool is_null() const = 0;
60 
61     virtual Result operator()(Args... args) const = 0;
62 
63     virtual void MoveInitializeTo(void* ptr) = 0;
64 };
65 
66 template <typename Result, typename... Args>
67 class NullFunctionTarget final : public FunctionTarget<Result, Args...> {
68 public:
69     NullFunctionTarget() = default;
70     ~NullFunctionTarget() final = default;
71 
72     DISALLOW_COPY_ASSIGN_AND_MOVE(NullFunctionTarget);
73 
74     bool is_null() const final { return true; }
75 
76     Result operator()(Args... args) const final {
77         ZX_PANIC("Attempted to invoke fbl::Function with a null target.");
78     }
79 
80     void MoveInitializeTo(void* ptr) final {
81         new (ptr) NullFunctionTarget();
82     }
83 };
84 
85 template <typename Callable, typename Result, typename... Args>
86 class InlineFunctionTarget final : public FunctionTarget<Result, Args...> {
87 public:
88     explicit InlineFunctionTarget(Callable target)
89         : target_(std::move(target)) {}
90     InlineFunctionTarget(Callable target, AllocChecker* ac)
91         : target_(std::move(target)) { ac->arm(0u, true); }
92     InlineFunctionTarget(InlineFunctionTarget&& other)
93         : target_(std::move(other.target_)) {}
94     ~InlineFunctionTarget() final = default;
95 
96     DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(InlineFunctionTarget);
97 
98     bool is_null() const final { return false; }
99 
100     Result operator()(Args... args) const final {
101         return target_(std::forward<Args>(args)...);
102     }
103 
104     void MoveInitializeTo(void* ptr) final {
105         new (ptr) InlineFunctionTarget(std::move(*this));
106     }
107 
108 private:
109     mutable Callable target_;
110 };
111 
112 template <typename Callable, typename Result, typename... Args>
113 class HeapFunctionTarget final : public FunctionTarget<Result, Args...> {
114 public:
115     explicit HeapFunctionTarget(Callable target)
116         : target_ptr_(fbl::make_unique<Callable>(std::move(target))) {}
117     HeapFunctionTarget(Callable target, AllocChecker* ac)
118         : target_ptr_(fbl::make_unique_checked<Callable>(ac, std::move(target))) {}
119     HeapFunctionTarget(HeapFunctionTarget&& other)
120         : target_ptr_(std::move(other.target_ptr_)) {}
121     ~HeapFunctionTarget() final = default;
122 
123     DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(HeapFunctionTarget);
124 
125     bool is_null() const final { return false; }
126 
127     Result operator()(Args... args) const final {
128         return (*target_ptr_)(std::forward<Args>(args)...);
129     }
130 
131     void MoveInitializeTo(void* ptr) final {
132         new (ptr) HeapFunctionTarget(std::move(*this));
133     }
134 
135 private:
136     fbl::unique_ptr<Callable> target_ptr_;
137 };
138 
139 // Holds a function target.
140 // If a callable object is small enough, it will be stored as an |InlineFunctionTarget|.
141 // Otherwise it will be stored as a |HeapFunctionTarget|.
142 template <size_t target_size, typename Result, typename... Args>
143 struct FunctionTargetHolder final {
144     FunctionTargetHolder() = default;
145 
146     DISALLOW_COPY_ASSIGN_AND_MOVE(FunctionTargetHolder);
147 
148     void InitializeNullTarget() {
149         using NullFunctionTarget = fbl::internal::NullFunctionTarget<Result, Args...>;
150         static_assert(sizeof(NullFunctionTarget) <= target_size,
151                       "NullFunctionTarget should fit in FunctionTargetHolder.");
152         new (&bits_) NullFunctionTarget();
153     }
154 
155     template <typename Callable>
156     struct TargetHelper {
157         using InlineFunctionTarget = fbl::internal::InlineFunctionTarget<Callable, Result, Args...>;
158         using HeapFunctionTarget = fbl::internal::HeapFunctionTarget<Callable, Result, Args...>;
159         static constexpr bool can_inline = (sizeof(InlineFunctionTarget) <= target_size);
160         using Type = typename fbl::conditional<can_inline, InlineFunctionTarget, HeapFunctionTarget>::type;
161         static_assert(sizeof(Type) <= target_size, "Target should fit in FunctionTargetHolder.");
162     };
163 
164     template <typename Callable>
165     void InitializeTarget(Callable target) {
166         new (&bits_) typename TargetHelper<Callable>::Type(std::move(target));
167     }
168 
169     template <typename Callable>
170     void InitializeTarget(Callable target, AllocChecker* ac) {
171         new (&bits_) typename TargetHelper<Callable>::Type(std::move(target), ac);
172     }
173 
174     void MoveInitializeTargetFrom(FunctionTargetHolder& other) {
175         other.target().MoveInitializeTo(&bits_);
176     }
177 
178     void DestroyTarget() {
179         target().~FunctionTarget();
180     }
181 
182     using FunctionTarget = fbl::internal::FunctionTarget<Result, Args...>;
183     FunctionTarget& target() { return *reinterpret_cast<FunctionTarget*>(&bits_); }
184     const FunctionTarget& target() const { return *reinterpret_cast<const FunctionTarget*>(&bits_); }
185 
186 private:
187     alignas(max_align_t) union { char data[target_size]; } bits_;
188 };
189 
190 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
191 class Function;
192 
193 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
194 class Function<inline_callable_size, require_inline, Result(Args...)> {
195     struct FakeCallable {
196         alignas(max_align_t) char bits[fbl::round_up(inline_callable_size, sizeof(void*))];
197     };
198     static constexpr size_t inline_target_size =
199         sizeof(InlineFunctionTarget<FakeCallable, Result, Args...>);
200     static constexpr size_t heap_target_size =
201         sizeof(HeapFunctionTarget<FakeCallable, Result, Args...>);
202     static constexpr size_t target_size = require_inline ? inline_target_size
203                                                          : fbl::max(inline_target_size, heap_target_size);
204     using TargetHolder = FunctionTargetHolder<target_size, Result, Args...>;
205 
206 public:
207     using result_type = Result;
208 
209     Function() { holder_.InitializeNullTarget(); }
210 
211     Function(decltype(nullptr)) { holder_.InitializeNullTarget(); }
212 
213     Function(Function&& other) {
214         holder_.MoveInitializeTargetFrom(other.holder_);
215         other.holder_.InitializeNullTarget();
216     }
217 
218     template <typename Callable>
219     Function(Callable target) {
220         InitializeTarget(std::move(target));
221     }
222 
223     template <typename Callable>
224     Function(Callable target, AllocChecker* ac) {
225         InitializeTarget(std::move(target), ac);
226     }
227 
228     ~Function() {
229         holder_.DestroyTarget();
230     }
231 
232     DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Function);
233 
234     explicit operator bool() const {
235         return !holder_.target().is_null();
236     };
237 
238     Result operator()(Args... args) const {
239         return holder_.target()(std::forward<Args>(args)...);
240     }
241 
242     Function& operator=(decltype(nullptr)) {
243         holder_.DestroyTarget();
244         holder_.InitializeNullTarget();
245         return *this;
246     }
247 
248     Function& operator=(Function&& other) {
249         holder_.DestroyTarget();
250         holder_.MoveInitializeTargetFrom(other.holder_);
251         other.holder_.InitializeNullTarget();
252         return *this;
253     }
254 
255     template <typename Callable>
256     Function& operator=(Callable target) {
257         SetTarget(std::move(target));
258         return *this;
259     }
260 
261     template <typename Callable>
262     void SetTarget(Callable target) {
263         holder_.DestroyTarget();
264         InitializeTarget(std::move(target));
265     }
266 
267     template <typename Callable>
268     void SetTarget(Callable target, AllocChecker* ac) {
269         holder_.DestroyTarget();
270         InitializeTarget(std::move(target), ac);
271     }
272 
273     void swap(Function& other) {
274         TargetHolder temp;
275         temp.MoveInitializeTargetFrom(holder_);
276         holder_.MoveInitializeTargetFrom(other.holder_);
277         other.holder_.MoveInitializeTargetFrom(temp);
278     }
279 
280 private:
281     template <typename Callable>
282     void InitializeTarget(Callable target) {
283         static_assert(!require_inline || sizeof(Callable) <= inline_callable_size,
284                       "Callable too large for InlineFunction.");
285         if (IsNull(target)) {
286             holder_.InitializeNullTarget();
287         } else {
288             holder_.InitializeTarget(std::move(target));
289         }
290     }
291 
292     template <typename Callable>
293     void InitializeTarget(Callable target, AllocChecker* ac) {
294         static_assert(!require_inline || sizeof(Callable) <= inline_callable_size,
295                       "Callable too large for InlineFunction.");
296         if (IsNull(target)) {
297             holder_.InitializeNullTarget();
298         } else {
299             holder_.InitializeTarget(std::move(target), ac);
300         }
301     }
302 
303     TargetHolder holder_;
304 };
305 
306 // Helper used by |BindMember| to invoke a pointer to member function.
307 template <typename R, typename T, typename... Args>
308 class MemberInvoker final {
309 public:
310     using MemFn = R (T::*)(Args...);
311 
312     MemberInvoker(T* instance, MemFn fn)
313         : instance_(instance), fn_(fn) {}
314 
315     R operator()(Args... args) const {
316         return (instance_->*fn_)(std::forward<Args>(args)...);
317     }
318 
319 private:
320     T* const instance_;
321     MemFn const fn_;
322 };
323 
324 } // namespace internal
325 
326 // The default size allowance for callable objects which can be inlined within
327 // a function object.  This default allows for inline storage of callables
328 // consisting of a function pointer and an object pointer (or similar callables
329 // of the same size).
330 constexpr size_t kDefaultInlineCallableSize = sizeof(void*) * 2;
331 
332 // A move-only callable object wrapper.
333 //
334 // fbl::Function<T> behaves like std::function<T> except that it is move-only
335 // instead of copyable.  This means it can hold mutable lambdas without requiring
336 // a reference-counted wrapper.
337 //
338 // Small callable objects (smaller than |kDefaultInlineCallableSize|) are stored
339 // inline within the function.  Larger callable objects will be copied to the heap
340 // if necessary.
341 //
342 // See also fbl::SizedFunction<T, size> and fbl::InlineFunction<T, size>
343 // for more control over allocation behavior.
344 //
345 // SYNOPSIS:
346 //
347 // template <typename Result, typename Args...>
348 // class Function<Result(Args...)> {
349 // public:
350 //     using result_type = Result;
351 //
352 //     Function();
353 //     explicit Function(decltype(nullptr));
354 //     Function(Function&& other);
355 //     template <typename Callable>
356 //     explicit Function(Callable target);
357 //     template <typename Callable>
358 //     explicit Function(Callable target, AllocChecker* ac);
359 //
360 //     ~Function();
361 //
362 //     explicit operator bool() const;
363 //
364 //     Result operator()(Args... args) const;
365 //
366 //     Function& operator=(decltype(nullptr));
367 //     Function& operator=(Function&& other);
368 //     template <typename Callable>
369 //     Function& operator=(Callable target);
370 //
371 //     template <typename Callable>
372 //     void SetTarget(Callable target);
373 //     template <typename Callable>
374 //     void SetTarget(Callable target, AllocChecker* ac);
375 //
376 //     void swap(Function& other);
377 // };
378 //
379 // EXAMPLE:
380 //
381 // using FoldFunction = fbl::Function<int(int value, int item)>;
382 //
383 // int FoldVector(const fbl::Vector<int>& in, int value, const FoldFunction& f) {
384 //     for (auto& item : in) {
385 //         value = f(value, item);
386 //     }
387 //     return value;
388 // }
389 //
390 // int SumItem(int value, int item) {
391 //     return value + item;
392 // }
393 //
394 // int Sum(const fbl::Vector<int>& in) {
395 //     // bind to a function pointer
396 //     FoldFunction sum(&SumItem);
397 //     return FoldVector(in, 0, sum);
398 // }
399 //
400 // int AlternatingSum(const fbl::Vector<int>& in) {
401 //     // bind to a lambda
402 //     int sign = 1;
403 //     FoldFunction alternating_sum([&sign](int value, int item) {
404 //         value += sign * item;
405 //         sign *= -1;
406 //         return value;
407 //     });
408 //     return FoldVector(in, 0, alternating_sum);
409 // }
410 //
411 template <typename T>
412 using Function = fbl::internal::Function<kDefaultInlineCallableSize, false, T>;
413 
414 // A move-only callable object wrapper with a explicitly specified (non-default)
415 // inline callable size preference.
416 //
417 // Behaves just like fbl::Function<T> except that it guarantees that callable
418 // objects of up to |inline_callable_size| bytes will be stored inline instead
419 // of on the heap.  This may be useful when you want to optimize storage of
420 // functions of a known size.
421 //
422 // Note that the effective maximum inline callable size may be slightly larger
423 // due to object alignment and rounding.
424 template <typename T, size_t inline_callable_size>
425 using SizedFunction = fbl::internal::Function<inline_callable_size, false, T>;
426 
427 // A move-only callable object wrapper which forces callables to be stored inline
428 // thereby preventing heap allocation.
429 //
430 // Behaves just like fbl::Function<T> except that it will refuse to store a
431 // callable object larger than |inline_callable_size| (will fail to compile).
432 template <typename T, size_t inline_callable_size>
433 using InlineFunction = fbl::internal::Function<inline_callable_size, true, T>;
434 
435 // Comparing functions with nullptr.
436 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
437 bool operator==(const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f,
438                 decltype(nullptr)) {
439     return !f;
440 }
441 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
442 bool operator!=(const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f,
443                 decltype(nullptr)) {
444     return !!f;
445 }
446 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
447 bool operator==(decltype(nullptr),
448                 const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f) {
449     return !f;
450 }
451 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args>
452 bool operator!=(decltype(nullptr),
453                 const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f) {
454     return !!f;
455 }
456 
457 // A function which takes no arguments and produces no result.
458 using Closure = fbl::Function<void()>;
459 
460 // Returns a Callable object which invokes a member function of an object.
461 //
462 // EXAMPLE:
463 //
464 // class Accumulator {
465 // public:
466 //     void Add(int value) {
467 //          sum += value;
468 //     }
469 //
470 //     int sum = 0;
471 // };
472 //
473 // void CountToTen(fbl::Function<void(int)> function) {
474 //     for (int i = 1; i <= 10; i++) {
475 //         function(i);
476 //     }
477 // }
478 //
479 // int SumToTen() {
480 //     Accumulator accum;
481 //     CountToTen(fbl::BindMember(&accum, &Accumulator::Add));
482 //     return accum.sum;
483 // }
484 template <typename R, typename T, typename... Args>
485 auto BindMember(T* instance, R (T::*fn)(Args...)) {
486     return internal::MemberInvoker<R, T, Args...>(instance, fn);
487 }
488 
489 } // namespace fbl
490