Home | History | Annotate | Line # | Download | only in gdbsupport
      1 /* Copyright (C) 2017-2024 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   There's also a gdb::make_function_view function that you can use to
    152   automatically create a function_view from a callable without having
    153   to specify the function_view's template parameter.  E.g.:
    154 
    155     auto lambda = [&] (int) { ... };
    156     auto fv = gdb::make_function_view (lambda);
    157 
    158   This can be useful for example when calling a template function
    159   whose function_view parameter type depends on the function's
    160   template parameters.  In such case, you can't rely on implicit
    161   callable->function_view conversion for the function_view argument.
    162   You must pass a function_view argument already of the right type to
    163   the template function.  E.g., with this:
    164 
    165     template<typename T>
    166     void my_function (T v, gdb::function_view<void(T)> callback = nullptr);
    167 
    168   this wouldn't compile:
    169 
    170     auto lambda = [&] (int) { ... };
    171     my_function (1, lambda);
    172 
    173   Note that this immediately dangles the temporary lambda object:
    174 
    175     gdb::function_view<void(int)> fv = [&] (int) { ... };  // dangles
    176     my_function (fv);
    177 
    178   To avoid the dangling you'd have to use a named temporary for the
    179   lambda:
    180 
    181     auto lambda = [&] (int) { ... };
    182     gdb::function_view<void(int)> fv = lambda;
    183     my_function (fv);
    184 
    185   Using gdb::make_function_view instead automatically deduces the
    186   function_view's full type, and, avoids worrying about dangling.  For
    187   the example above, we could write instead:
    188 
    189     auto lambda = [&] (int) { ... };
    190     my_function (1, gdb::make_function_view (lambda));
    191 
    192   You can find unit tests covering the whole API in
    193   unittests/function-view-selftests.c.  */
    194 
    195 #include <type_traits>
    196 namespace gdb {
    197 
    198 namespace fv_detail {
    199 /* Bits shared by all function_view instantiations that do not depend
    200    on the template parameters.  */
    201 
    202 /* Storage for the erased callable.  This is a union in order to be
    203    able to save both a function object (data) pointer or a function
    204    pointer without triggering undefined behavior.  */
    205 union erased_callable
    206 {
    207   /* For function objects.  */
    208   void *data;
    209 
    210     /* For function pointers.  */
    211   void (*fn) ();
    212 };
    213 
    214 } /* namespace fv_detail */
    215 
    216 /* Use partial specialization to get access to the callable's
    217    signature. */
    218 template<class Signature>
    219 struct function_view;
    220 
    221 template<typename Res, typename... Args>
    222 class function_view<Res (Args...)>
    223 {
    224   template<typename From, typename To>
    225   using CompatibleReturnType
    226     = Or<std::is_void<To>,
    227 	 std::is_same<From, To>,
    228 	 std::is_convertible<From, To>>;
    229 
    230   /* True if Func can be called with Args, and either the result is
    231      Res, convertible to Res or Res is void.  */
    232   template<typename Callable,
    233 	   typename Res2 = typename std::invoke_result<Callable &, Args...>::type>
    234   struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
    235   {};
    236 
    237   /* True if Callable is a function_view.  Used to avoid hijacking the
    238      copy ctor.  */
    239   template <typename Callable>
    240   struct IsFunctionView
    241     : std::is_same<function_view, typename std::decay<Callable>::type>
    242   {};
    243 
    244  public:
    245 
    246   /* NULL by default.  */
    247   constexpr function_view () noexcept
    248     : m_erased_callable {},
    249       m_invoker {}
    250   {}
    251 
    252   /* Default copy/assignment is fine.  */
    253   function_view (const function_view &) = default;
    254   function_view &operator= (const function_view &) = default;
    255 
    256   /* This is the main entry point.  Use SFINAE to avoid hijacking the
    257      copy constructor and to ensure that the target type is
    258      compatible.  */
    259   template
    260     <typename Callable,
    261      typename = Requires<Not<IsFunctionView<Callable>>>,
    262      typename = Requires<IsCompatibleCallable<Callable>>>
    263   function_view (Callable &&callable) noexcept
    264   {
    265     bind (callable);
    266   }
    267 
    268   /* Construct a NULL function_view.  */
    269   constexpr function_view (std::nullptr_t) noexcept
    270     : m_erased_callable {},
    271       m_invoker {}
    272   {}
    273 
    274   /* Clear a function_view.  */
    275   function_view &operator= (std::nullptr_t) noexcept
    276   {
    277     m_invoker = nullptr;
    278     return *this;
    279   }
    280 
    281   /* Return true if the wrapper has a target, false otherwise.  Note
    282      we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
    283      know which member of the union is active right now.  */
    284   constexpr explicit operator bool () const noexcept
    285   { return m_invoker != nullptr; }
    286 
    287   /* Call the callable.  */
    288   Res operator () (Args... args) const
    289   { return m_invoker (m_erased_callable, std::forward<Args> (args)...); }
    290 
    291  private:
    292 
    293   /* Bind this function_view to a compatible function object
    294      reference.  */
    295   template <typename Callable>
    296   void bind (Callable &callable) noexcept
    297   {
    298     m_erased_callable.data = (void *) std::addressof (callable);
    299     m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
    300       noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
    301       {
    302 	auto &restored_callable = *static_cast<Callable *> (ecall.data);
    303 	/* The explicit cast to Res avoids a compile error when Res is
    304 	   void and the callable returns non-void.  */
    305 	return (Res) restored_callable (std::forward<Args> (args)...);
    306       };
    307   }
    308 
    309   /* Bind this function_view to a compatible function pointer.
    310 
    311      Making this a separate function allows avoiding one indirection,
    312      by storing the function pointer directly in the storage, instead
    313      of a pointer to pointer.  erased_callable is then a union in
    314      order to avoid storing a function pointer as a data pointer here,
    315      which would be undefined.  */
    316   template<class Res2, typename... Args2>
    317   void bind (Res2 (*fn) (Args2...)) noexcept
    318   {
    319     m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
    320     m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
    321       noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
    322       {
    323 	auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
    324 	/* The explicit cast to Res avoids a compile error when Res is
    325 	   void and the callable returns non-void.  */
    326 	return (Res) restored_fn (std::forward<Args> (args)...);
    327       };
    328   }
    329 
    330   /* Storage for the erased callable.  */
    331   fv_detail::erased_callable m_erased_callable;
    332 
    333   /* The invoker.  This is set to a capture-less lambda by one of the
    334      'bind' overloads.  The lambda restores the right type of the
    335      callable (which is passed as first argument), and forwards the
    336      args.  */
    337   Res (*m_invoker) (fv_detail::erased_callable, Args...);
    338 };
    339 
    340 /* Allow comparison with NULL.  Defer the work to the in-class
    341    operator bool implementation.  */
    342 
    343 template<typename Res, typename... Args>
    344 constexpr inline bool
    345 operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
    346 { return !static_cast<bool> (f); }
    347 
    348 template<typename Res, typename... Args>
    349 constexpr inline bool
    350 operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
    351 { return !static_cast<bool> (f); }
    352 
    353 template<typename Res, typename... Args>
    354 constexpr inline bool
    355 operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
    356 { return static_cast<bool> (f); }
    357 
    358 template<typename Res, typename... Args>
    359 constexpr inline bool
    360 operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
    361 { return static_cast<bool> (f); }
    362 
    363 namespace fv_detail {
    364 
    365 /* Helper traits type to automatically find the right function_view
    366    type for a callable.  */
    367 
    368 /* Use partial specialization to get access to the callable's
    369    signature, for all the different callable variants.  */
    370 
    371 template<typename>
    372 struct function_view_traits;
    373 
    374 /* Main partial specialization with plain function signature type.
    375    All others end up redirected here.  */
    376 template<typename Res, typename... Args>
    377 struct function_view_traits<Res (Args...)>
    378 {
    379   using type = gdb::function_view<Res (Args...)>;
    380 };
    381 
    382 /* Function pointers.  */
    383 template<typename Res, typename... Args>
    384 struct function_view_traits<Res (*) (Args...)>
    385   : function_view_traits<Res (Args...)>
    386 {
    387 };
    388 
    389 /* Function references.  */
    390 template<typename Res, typename... Args>
    391 struct function_view_traits<Res (&) (Args...)>
    392   : function_view_traits<Res (Args...)>
    393 {
    394 };
    395 
    396 /* Reference to function pointers.  */
    397 template<typename Res, typename... Args>
    398 struct function_view_traits<Res (*&) (Args...)>
    399   : function_view_traits<Res (Args...)>
    400 {
    401 };
    402 
    403 /* Reference to const function pointers.  */
    404 template<typename Res, typename... Args>
    405 struct function_view_traits<Res (* const &) (Args...)>
    406   : function_view_traits<Res (Args...)>
    407 {
    408 };
    409 
    410 /* Const member functions.  function_view doesn't support these, but
    411    we need this in order to extract the type of function objects.
    412    Lambdas pass here, after starting at the operator() case,
    413    below.  */
    414 template<typename Res, typename Class, typename... Args>
    415 struct function_view_traits<Res (Class::*) (Args...) const>
    416   : function_view_traits<Res (Args...)>
    417 {
    418 };
    419 
    420 /* Member functions.  Ditto, for function objects with non-const
    421    operator().  */
    422 template<typename Res, typename Class, typename... Args>
    423 struct function_view_traits<Res (Class::*) (Args...)>
    424   : function_view_traits<Res (Args...)>
    425 {
    426 };
    427 
    428 /* Function objects, lambdas, std::function, any type that defines
    429    operator().  */
    430 template<typename FuncObj>
    431 struct function_view_traits
    432   : function_view_traits <decltype
    433 			  (&std::remove_reference<FuncObj>::type::operator())>
    434 {
    435 };
    436 
    437 } /* namespace fv_detail */
    438 
    439 /* Make a function_view from a callable.  Useful to automatically
    440    deduce the function_view's template argument type.  */
    441 template<typename Callable>
    442 auto make_function_view (Callable &&callable)
    443   -> typename fv_detail::function_view_traits<Callable>::type
    444 {
    445   using fv = typename fv_detail::function_view_traits<Callable>::type;
    446   return fv (std::forward<Callable> (callable));
    447 }
    448 
    449 } /* namespace gdb */
    450 
    451 #endif
    452