| 1 | /* Copyright (C) 2017-2020 Free Software Foundation, Inc. |
| 2 | |
| 3 | This file is part of GDB. |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | This program is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 17 | |
| 18 | #ifndef COMMON_FUNCTION_VIEW_H |
| 19 | #define COMMON_FUNCTION_VIEW_H |
| 20 | |
| 21 | /* function_view is a polymorphic type-erasing wrapper class that |
| 22 | encapsulates a non-owning reference to arbitrary callable objects. |
| 23 | |
| 24 | A way to put it is that function_view is to std::function like |
| 25 | std::string_view is to std::string. While std::function stores a |
| 26 | type-erased callable object internally, function_view holds a |
| 27 | type-erased reference to an external callable object. |
| 28 | |
| 29 | This is meant to be used as callback type of a function that: |
| 30 | |
| 31 | #1 - Takes a callback as parameter. |
| 32 | |
| 33 | #2 - Wants to support arbitrary callable objects as callback type |
| 34 | (e.g., stateful function objects, lambda closures, free |
| 35 | functions). |
| 36 | |
| 37 | #3 - Does not store the callback anywhere; instead the function |
| 38 | just calls the callback directly or forwards it to some |
| 39 | other function that calls it. |
| 40 | |
| 41 | #4 - Can't be, or we don't want it to be, a template function |
| 42 | with the callable type as template parameter. For example, |
| 43 | when the callback is a parameter of a virtual member |
| 44 | function, or when putting the function template in a header |
| 45 | would expose too much implementation detail. |
| 46 | |
| 47 | Note that the C-style "function pointer" + "void *data" callback |
| 48 | parameter idiom fails requirement #2 above. Please don't add new |
| 49 | uses of that idiom. I.e., something like this wouldn't work; |
| 50 | |
| 51 | typedef bool (iterate_over_foos_cb) (foo *f, void *user_data), |
| 52 | void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data); |
| 53 | |
| 54 | foo *find_foo_by_type (int type) |
| 55 | { |
| 56 | foo *found = nullptr; |
| 57 | |
| 58 | iterate_over_foos ([&] (foo *f, void *data) |
| 59 | { |
| 60 | if (foo->type == type) |
| 61 | { |
| 62 | found = foo; |
| 63 | return true; // stop iterating |
| 64 | } |
| 65 | return false; // continue iterating |
| 66 | }, NULL); |
| 67 | |
| 68 | return found; |
| 69 | } |
| 70 | |
| 71 | The above wouldn't compile, because lambdas with captures can't be |
| 72 | implicitly converted to a function pointer (because a capture means |
| 73 | some context data must be passed to the lambda somehow). |
| 74 | |
| 75 | C++11 gave us std::function as type-erased wrapper around arbitrary |
| 76 | callables, however, std::function is not an ideal fit for transient |
| 77 | callbacks such as the use case above. For this use case, which is |
| 78 | quite pervasive, a function_view is a better choice, because while |
| 79 | function_view is light and does not require any heap allocation, |
| 80 | std::function is a heavy-weight object with value semantics that |
| 81 | generally requires a heap allocation on construction/assignment of |
| 82 | the target callable. In addition, while it is possible to use |
| 83 | std::function in such a way that avoids most of the overhead by |
| 84 | making sure to only construct it with callables of types that fit |
| 85 | std::function's small object optimization, such as function |
| 86 | pointers and std::reference_wrapper callables, that is quite |
| 87 | inconvenient in practice, because restricting to free-function |
| 88 | callables would imply no state/capture/closure, which we need in |
| 89 | most cases, and std::reference_wrapper implies remembering to use |
| 90 | std::ref/std::cref where the callable is constructed, with the |
| 91 | added inconvenience that std::ref/std::cref have deleted rvalue-ref |
| 92 | overloads, meaning you can't use unnamed/temporary lambdas with |
| 93 | them. |
| 94 | |
| 95 | Note that because function_view is a non-owning view of a callable, |
| 96 | care must be taken to ensure that the callable outlives the |
| 97 | function_view that calls it. This is not really a problem for the |
| 98 | use case function_view is intended for, such as passing a temporary |
| 99 | function object / lambda to a function that accepts a callback, |
| 100 | because in those cases, the temporary is guaranteed to be live |
| 101 | until the called function returns. |
| 102 | |
| 103 | Calling a function_view with no associated target is undefined, |
| 104 | unlike with std::function, which throws std::bad_function_call. |
| 105 | This is by design, to avoid the otherwise necessary NULL check in |
| 106 | function_view::operator(). |
| 107 | |
| 108 | Since function_view objects are small (a pair of pointers), they |
| 109 | should generally be passed around by value. |
| 110 | |
| 111 | Usage: |
| 112 | |
| 113 | Given this function that accepts a callback: |
| 114 | |
| 115 | void |
| 116 | iterate_over_foos (gdb::function_view<void (foo *)> callback) |
| 117 | { |
| 118 | for (auto &foo : foos) |
| 119 | callback (&foo); |
| 120 | } |
| 121 | |
| 122 | you can call it like this, passing a lambda as callback: |
| 123 | |
| 124 | iterate_over_foos ([&] (foo *f) |
| 125 | { |
| 126 | process_one_foo (f); |
| 127 | }); |
| 128 | |
| 129 | or like this, passing a function object as callback: |
| 130 | |
| 131 | struct function_object |
| 132 | { |
| 133 | void operator() (foo *f) |
| 134 | { |
| 135 | if (s->check ()) |
| 136 | process_one_foo (f); |
| 137 | } |
| 138 | |
| 139 | // some state |
| 140 | state *s; |
| 141 | }; |
| 142 | |
| 143 | state mystate; |
| 144 | function_object matcher {&mystate}; |
| 145 | iterate_over_foos (matcher); |
| 146 | |
| 147 | or like this, passing a function pointer as callback: |
| 148 | |
| 149 | iterate_over_foos (process_one_foo); |
| 150 | |
| 151 | You can find unit tests covering the whole API in |
| 152 | unittests/function-view-selftests.c. */ |
| 153 | |
| 154 | namespace gdb { |
| 155 | |
| 156 | namespace fv_detail { |
| 157 | /* Bits shared by all function_view instantiations that do not depend |
| 158 | on the template parameters. */ |
| 159 | |
| 160 | /* Storage for the erased callable. This is a union in order to be |
| 161 | able to save both a function object (data) pointer or a function |
| 162 | pointer without triggering undefined behavior. */ |
| 163 | union erased_callable |
| 164 | { |
| 165 | /* For function objects. */ |
| 166 | void *data; |
| 167 | |
| 168 | /* For function pointers. */ |
| 169 | void (*fn) (); |
| 170 | }; |
| 171 | |
| 172 | } /* namespace fv_detail */ |
| 173 | |
| 174 | /* Use partial specialization to get access to the callable's |
| 175 | signature. */ |
| 176 | template<class Signature> |
| 177 | struct function_view; |
| 178 | |
| 179 | template<typename Res, typename... Args> |
| 180 | class function_view<Res (Args...)> |
| 181 | { |
| 182 | template<typename From, typename To> |
| 183 | using CompatibleReturnType |
| 184 | = Or<std::is_void<To>, |
| 185 | std::is_same<From, To>, |
| 186 | std::is_convertible<From, To>>; |
| 187 | |
| 188 | /* True if Func can be called with Args, and either the result is |
| 189 | Res, convertible to Res or Res is void. */ |
| 190 | template<typename Callable, |
| 191 | typename Res2 = typename std::result_of<Callable &(Args...)>::type> |
| 192 | struct IsCompatibleCallable : CompatibleReturnType<Res2, Res> |
| 193 | {}; |
| 194 | |
| 195 | /* True if Callable is a function_view. Used to avoid hijacking the |
| 196 | copy ctor. */ |
| 197 | template <typename Callable> |
| 198 | struct IsFunctionView |
| 199 | : std::is_same<function_view, typename std::decay<Callable>::type> |
| 200 | {}; |
| 201 | |
| 202 | public: |
| 203 | |
| 204 | /* NULL by default. */ |
| 205 | constexpr function_view () noexcept |
| 206 | : m_erased_callable {}, |
| 207 | m_invoker {} |
| 208 | {} |
| 209 | |
| 210 | /* Default copy/assignment is fine. */ |
| 211 | function_view (const function_view &) = default; |
| 212 | function_view &operator= (const function_view &) = default; |
| 213 | |
| 214 | /* This is the main entry point. Use SFINAE to avoid hijacking the |
| 215 | copy constructor and to ensure that the target type is |
| 216 | compatible. */ |
| 217 | template |
| 218 | <typename Callable, |
| 219 | typename = Requires<Not<IsFunctionView<Callable>>>, |
| 220 | typename = Requires<IsCompatibleCallable<Callable>>> |
| 221 | function_view (Callable &&callable) noexcept |
| 222 | { |
| 223 | bind (callable); |
| 224 | } |
| 225 | |
| 226 | /* Construct a NULL function_view. */ |
| 227 | constexpr function_view (std::nullptr_t) noexcept |
| 228 | : m_erased_callable {}, |
| 229 | m_invoker {} |
| 230 | {} |
| 231 | |
| 232 | /* Clear a function_view. */ |
| 233 | function_view &operator= (std::nullptr_t) noexcept |
| 234 | { |
| 235 | m_invoker = nullptr; |
| 236 | return *this; |
| 237 | } |
| 238 | |
| 239 | /* Return true if the wrapper has a target, false otherwise. Note |
| 240 | we check M_INVOKER instead of M_ERASED_CALLABLE because we don't |
| 241 | know which member of the union is active right now. */ |
| 242 | constexpr explicit operator bool () const noexcept |
| 243 | { return m_invoker != nullptr; } |
| 244 | |
| 245 | /* Call the callable. */ |
| 246 | Res operator () (Args... args) const |
| 247 | { return m_invoker (m_erased_callable, std::forward<Args> (args)...); } |
| 248 | |
| 249 | private: |
| 250 | |
| 251 | /* Bind this function_view to a compatible function object |
| 252 | reference. */ |
| 253 | template <typename Callable> |
| 254 | void bind (Callable &callable) noexcept |
| 255 | { |
| 256 | m_erased_callable.data = (void *) std::addressof (callable); |
| 257 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) |
| 258 | noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res |
| 259 | { |
| 260 | auto &restored_callable = *static_cast<Callable *> (ecall.data); |
| 261 | /* The explicit cast to Res avoids a compile error when Res is |
| 262 | void and the callable returns non-void. */ |
| 263 | return (Res) restored_callable (std::forward<Args> (args)...); |
| 264 | }; |
| 265 | } |
| 266 | |
| 267 | /* Bind this function_view to a compatible function pointer. |
| 268 | |
| 269 | Making this a separate function allows avoiding one indirection, |
| 270 | by storing the function pointer directly in the storage, instead |
| 271 | of a pointer to pointer. erased_callable is then a union in |
| 272 | order to avoid storing a function pointer as a data pointer here, |
| 273 | which would be undefined. */ |
| 274 | template<class Res2, typename... Args2> |
| 275 | void bind (Res2 (*fn) (Args2...)) noexcept |
| 276 | { |
| 277 | m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn); |
| 278 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) |
| 279 | noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res |
| 280 | { |
| 281 | auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn); |
| 282 | /* The explicit cast to Res avoids a compile error when Res is |
| 283 | void and the callable returns non-void. */ |
| 284 | return (Res) restored_fn (std::forward<Args> (args)...); |
| 285 | }; |
| 286 | } |
| 287 | |
| 288 | /* Storage for the erased callable. */ |
| 289 | fv_detail::erased_callable m_erased_callable; |
| 290 | |
| 291 | /* The invoker. This is set to a capture-less lambda by one of the |
| 292 | 'bind' overloads. The lambda restores the right type of the |
| 293 | callable (which is passed as first argument), and forwards the |
| 294 | args. */ |
| 295 | Res (*m_invoker) (fv_detail::erased_callable, Args...); |
| 296 | }; |
| 297 | |
| 298 | /* Allow comparison with NULL. Defer the work to the in-class |
| 299 | operator bool implementation. */ |
| 300 | |
| 301 | template<typename Res, typename... Args> |
| 302 | constexpr inline bool |
| 303 | operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept |
| 304 | { return !static_cast<bool> (f); } |
| 305 | |
| 306 | template<typename Res, typename... Args> |
| 307 | constexpr inline bool |
| 308 | operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept |
| 309 | { return !static_cast<bool> (f); } |
| 310 | |
| 311 | template<typename Res, typename... Args> |
| 312 | constexpr inline bool |
| 313 | operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept |
| 314 | { return static_cast<bool> (f); } |
| 315 | |
| 316 | template<typename Res, typename... Args> |
| 317 | constexpr inline bool |
| 318 | operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept |
| 319 | { return static_cast<bool> (f); } |
| 320 | |
| 321 | } /* namespace gdb */ |
| 322 | |
| 323 | #endif |