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_INTERNAL_H_
6 #define LIB_FIT_FUNCTION_INTERNAL_H_
7 
8 #include <stddef.h>
9 #include <stdlib.h>
10 
11 #include <new>
12 #include <type_traits>
13 #include <utility>
14 
15 namespace fit {
16 namespace internal {
17 
18 template <typename Result, typename... Args>
19 struct target_ops final {
20     void* (*get)(void* bits);
21     Result (*invoke)(void* bits, Args... args);
22     void (*move)(void* from_bits, void* to_bits);
23     void (*destroy)(void* bits);
24 };
25 
26 template <typename Callable, bool is_inline, typename Result, typename... Args>
27 struct target;
28 
29 template <typename Result, typename... Args>
30 struct target<decltype(nullptr), true, Result, Args...> final {
31     static Result invoke(void* bits, Args... args) {
32         abort();
33     }
34 
35     static const target_ops<Result, Args...> ops;
36 };
37 
38 inline void* null_target_get(void* bits) {
39     return nullptr;
40 }
41 inline void null_target_move(void* from_bits, void* to_bits) {}
42 inline void null_target_destroy(void* bits) {}
43 
44 template <typename Result, typename... Args>
45 constexpr target_ops<Result, Args...> target<decltype(nullptr), true, Result, Args...>::ops = {
46     &null_target_get,
47     &target::invoke,
48     &null_target_move,
49     &null_target_destroy};
50 
51 template <typename Callable, typename Result, typename... Args>
52 struct target<Callable, true, Result, Args...> final {
53     static void initialize(void* bits, Callable&& target) {
54         new (bits) Callable(std::move(target));
55     }
56     static Result invoke(void* bits, Args... args) {
57         auto& target = *static_cast<Callable*>(bits);
58         return target(std::forward<Args>(args)...);
59     }
60     static void move(void* from_bits, void* to_bits) {
61         auto& from_target = *static_cast<Callable*>(from_bits);
62         new (to_bits) Callable(std::move(from_target));
63         from_target.~Callable();
64     }
65     static void destroy(void* bits) {
66         auto& target = *static_cast<Callable*>(bits);
67         target.~Callable();
68     }
69 
70     static const target_ops<Result, Args...> ops;
71 };
72 
73 inline void* inline_target_get(void* bits) {
74     return bits;
75 }
76 
77 template <typename Callable, typename Result, typename... Args>
78 constexpr target_ops<Result, Args...> target<Callable, true, Result, Args...>::ops = {
79     &inline_target_get,
80     &target::invoke,
81     &target::move,
82     &target::destroy};
83 
84 template <typename Callable, typename Result, typename... Args>
85 struct target<Callable, false, Result, Args...> final {
86     static void initialize(void* bits, Callable&& target) {
87         auto ptr = static_cast<Callable**>(bits);
88         *ptr = new Callable(std::move(target));
89     }
90     static Result invoke(void* bits, Args... args) {
91         auto& target = **static_cast<Callable**>(bits);
92         return target(std::forward<Args>(args)...);
93     }
94     static void move(void* from_bits, void* to_bits) {
95         auto from_ptr = static_cast<Callable**>(from_bits);
96         auto to_ptr = static_cast<Callable**>(to_bits);
97         *to_ptr = *from_ptr;
98     }
99     static void destroy(void* bits) {
100         auto ptr = static_cast<Callable**>(bits);
101         delete *ptr;
102     }
103 
104     static const target_ops<Result, Args...> ops;
105 };
106 
107 inline void* heap_target_get(void* bits) {
108     return *static_cast<void**>(bits);
109 }
110 
111 template <typename Callable, typename Result, typename... Args>
112 constexpr target_ops<Result, Args...> target<Callable, false, Result, Args...>::ops = {
113     &heap_target_get,
114     &target::invoke,
115     &target::move,
116     &target::destroy};
117 
118 } // namespace internal
119 } // namespace fit
120 
121 #endif // LIB_FIT_FUNCTION_INTERNAL_H_
122