1 /* Copyright (C) 2017-2021 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. */ function_view()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>>> function_view(Callable && callable)221 function_view (Callable &&callable) noexcept 222 { 223 bind (callable); 224 } 225 226 /* Construct a NULL function_view. */ function_view(std::nullptr_t)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. */ operator()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> bind(Callable & 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> bind(Res2 (* fn)(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 324