/* Copyright (C) 2017-2024 Free Software Foundation, Inc.

  This file is part of GDB.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef GDBSUPPORT_FUNCTION_VIEW_H
#define GDBSUPPORT_FUNCTION_VIEW_H

/* function_view is a polymorphic type-erasing wrapper class that
  encapsulates a non-owning reference to arbitrary callable objects.

  A way to put it is that function_view is to std::function like
  std::string_view is to std::string.  While std::function stores a
  type-erased callable object internally, function_view holds a
  type-erased reference to an external callable object.

  This is meant to be used as callback type of a function that:

    #1 - Takes a callback as parameter.

    #2 - Wants to support arbitrary callable objects as callback type
         (e.g., stateful function objects, lambda closures, free
         functions).

    #3 - Does not store the callback anywhere; instead the function
         just calls the callback directly or forwards it to some
         other function that calls it.

    #4 - Can't be, or we don't want it to be, a template function
         with the callable type as template parameter.  For example,
         when the callback is a parameter of a virtual member
         function, or when putting the function template in a header
         would expose too much implementation detail.

  Note that the C-style "function pointer" + "void *data" callback
  parameter idiom fails requirement #2 above.  Please don't add new
  uses of that idiom.  I.e., something like this wouldn't work;

   typedef bool (iterate_over_foos_cb) (foo *f, void *user_data),
   void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data);

   foo *find_foo_by_type (int type)
   {
     foo *found = nullptr;

     iterate_over_foos ([&] (foo *f, void *data)
       {
         if (foo->type == type)
           {
             found = foo;
             return true; // stop iterating
           }
         return false; // continue iterating
       }, NULL);

     return found;
   }

  The above wouldn't compile, because lambdas with captures can't be
  implicitly converted to a function pointer (because a capture means
  some context data must be passed to the lambda somehow).

  C++11 gave us std::function as type-erased wrapper around arbitrary
  callables, however, std::function is not an ideal fit for transient
  callbacks such as the use case above.  For this use case, which is
  quite pervasive, a function_view is a better choice, because while
  function_view is light and does not require any heap allocation,
  std::function is a heavy-weight object with value semantics that
  generally requires a heap allocation on construction/assignment of
  the target callable.  In addition, while it is possible to use
  std::function in such a way that avoids most of the overhead by
  making sure to only construct it with callables of types that fit
  std::function's small object optimization, such as function
  pointers and std::reference_wrapper callables, that is quite
  inconvenient in practice, because restricting to free-function
  callables would imply no state/capture/closure, which we need in
  most cases, and std::reference_wrapper implies remembering to use
  std::ref/std::cref where the callable is constructed, with the
  added inconvenience that std::ref/std::cref have deleted rvalue-ref
  overloads, meaning you can't use unnamed/temporary lambdas with
  them.

  Note that because function_view is a non-owning view of a callable,
  care must be taken to ensure that the callable outlives the
  function_view that calls it.  This is not really a problem for the
  use case function_view is intended for, such as passing a temporary
  function object / lambda to a function that accepts a callback,
  because in those cases, the temporary is guaranteed to be live
  until the called function returns.

  Calling a function_view with no associated target is undefined,
  unlike with std::function, which throws std::bad_function_call.
  This is by design, to avoid the otherwise necessary NULL check in
  function_view::operator().

  Since function_view objects are small (a pair of pointers), they
  should generally be passed around by value.

  Usage:

  Given this function that accepts a callback:

   void
   iterate_over_foos (gdb::function_view<void (foo *)> callback)
   {
      for (auto &foo : foos)
        callback (&foo);
   }

  you can call it like this, passing a lambda as callback:

   iterate_over_foos ([&] (foo *f)
     {
       process_one_foo (f);
     });

  or like this, passing a function object as callback:

   struct function_object
   {
     void operator() (foo *f)
     {
       if (s->check ())
         process_one_foo (f);
     }

     // some state
     state *s;
   };

   state mystate;
   function_object matcher {&mystate};
   iterate_over_foos (matcher);

 or like this, passing a function pointer as callback:

   iterate_over_foos (process_one_foo);

 There's also a gdb::make_function_view function that you can use to
 automatically create a function_view from a callable without having
 to specify the function_view's template parameter.  E.g.:

   auto lambda = [&] (int) { ... };
   auto fv = gdb::make_function_view (lambda);

 This can be useful for example when calling a template function
 whose function_view parameter type depends on the function's
 template parameters.  In such case, you can't rely on implicit
 callable->function_view conversion for the function_view argument.
 You must pass a function_view argument already of the right type to
 the template function.  E.g., with this:

   template<typename T>
   void my_function (T v, gdb::function_view<void(T)> callback = nullptr);

 this wouldn't compile:

   auto lambda = [&] (int) { ... };
   my_function (1, lambda);

 Note that this immediately dangles the temporary lambda object:

   gdb::function_view<void(int)> fv = [&] (int) { ... };  // dangles
   my_function (fv);

 To avoid the dangling you'd have to use a named temporary for the
 lambda:

   auto lambda = [&] (int) { ... };
   gdb::function_view<void(int)> fv = lambda;
   my_function (fv);

 Using gdb::make_function_view instead automatically deduces the
 function_view's full type, and, avoids worrying about dangling.  For
 the example above, we could write instead:

   auto lambda = [&] (int) { ... };
   my_function (1, gdb::make_function_view (lambda));

 You can find unit tests covering the whole API in
 unittests/function-view-selftests.c.  */

#include <type_traits>
namespace gdb {

namespace fv_detail {
/* Bits shared by all function_view instantiations that do not depend
  on the template parameters.  */

/* Storage for the erased callable.  This is a union in order to be
  able to save both a function object (data) pointer or a function
  pointer without triggering undefined behavior.  */
union erased_callable
{
 /* For function objects.  */
 void *data;

   /* For function pointers.  */
 void (*fn) ();
};

} /* namespace fv_detail */

/* Use partial specialization to get access to the callable's
  signature. */
template<class Signature>
struct function_view;

template<typename Res, typename... Args>
class function_view<Res (Args...)>
{
 template<typename From, typename To>
 using CompatibleReturnType
   = Or<std::is_void<To>,
        std::is_same<From, To>,
        std::is_convertible<From, To>>;

 /* True if Func can be called with Args, and either the result is
    Res, convertible to Res or Res is void.  */
 template<typename Callable,
          typename Res2 = typename std::invoke_result<Callable &, Args...>::type>
 struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
 {};

 /* True if Callable is a function_view.  Used to avoid hijacking the
    copy ctor.  */
 template <typename Callable>
 struct IsFunctionView
   : std::is_same<function_view, typename std::decay<Callable>::type>
 {};

public:

 /* NULL by default.  */
 constexpr function_view () noexcept
   : m_erased_callable {},
     m_invoker {}
 {}

 /* Default copy/assignment is fine.  */
 function_view (const function_view &) = default;
 function_view &operator= (const function_view &) = default;

 /* This is the main entry point.  Use SFINAE to avoid hijacking the
    copy constructor and to ensure that the target type is
    compatible.  */
 template
   <typename Callable,
    typename = Requires<Not<IsFunctionView<Callable>>>,
    typename = Requires<IsCompatibleCallable<Callable>>>
 function_view (Callable &&callable) noexcept
 {
   bind (callable);
 }

 /* Construct a NULL function_view.  */
 constexpr function_view (std::nullptr_t) noexcept
   : m_erased_callable {},
     m_invoker {}
 {}

 /* Clear a function_view.  */
 function_view &operator= (std::nullptr_t) noexcept
 {
   m_invoker = nullptr;
   return *this;
 }

 /* Return true if the wrapper has a target, false otherwise.  Note
    we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
    know which member of the union is active right now.  */
 constexpr explicit operator bool () const noexcept
 { return m_invoker != nullptr; }

 /* Call the callable.  */
 Res operator () (Args... args) const
 { return m_invoker (m_erased_callable, std::forward<Args> (args)...); }

private:

 /* Bind this function_view to a compatible function object
    reference.  */
 template <typename Callable>
 void bind (Callable &callable) noexcept
 {
   m_erased_callable.data = (void *) std::addressof (callable);
   m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
     noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
     {
       auto &restored_callable = *static_cast<Callable *> (ecall.data);
       /* The explicit cast to Res avoids a compile error when Res is
          void and the callable returns non-void.  */
       return (Res) restored_callable (std::forward<Args> (args)...);
     };
 }

 /* Bind this function_view to a compatible function pointer.

    Making this a separate function allows avoiding one indirection,
    by storing the function pointer directly in the storage, instead
    of a pointer to pointer.  erased_callable is then a union in
    order to avoid storing a function pointer as a data pointer here,
    which would be undefined.  */
 template<class Res2, typename... Args2>
 void bind (Res2 (*fn) (Args2...)) noexcept
 {
   m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
   m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
     noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
     {
       auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
       /* The explicit cast to Res avoids a compile error when Res is
          void and the callable returns non-void.  */
       return (Res) restored_fn (std::forward<Args> (args)...);
     };
 }

 /* Storage for the erased callable.  */
 fv_detail::erased_callable m_erased_callable;

 /* The invoker.  This is set to a capture-less lambda by one of the
    'bind' overloads.  The lambda restores the right type of the
    callable (which is passed as first argument), and forwards the
    args.  */
 Res (*m_invoker) (fv_detail::erased_callable, Args...);
};

/* Allow comparison with NULL.  Defer the work to the in-class
  operator bool implementation.  */

template<typename Res, typename... Args>
constexpr inline bool
operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
{ return !static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
{ return !static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
{ return static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
{ return static_cast<bool> (f); }

namespace fv_detail {

/* Helper traits type to automatically find the right function_view
  type for a callable.  */

/* Use partial specialization to get access to the callable's
  signature, for all the different callable variants.  */

template<typename>
struct function_view_traits;

/* Main partial specialization with plain function signature type.
  All others end up redirected here.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (Args...)>
{
 using type = gdb::function_view<Res (Args...)>;
};

/* Function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (*) (Args...)>
 : function_view_traits<Res (Args...)>
{
};

/* Function references.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (&) (Args...)>
 : function_view_traits<Res (Args...)>
{
};

/* Reference to function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (*&) (Args...)>
 : function_view_traits<Res (Args...)>
{
};

/* Reference to const function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (* const &) (Args...)>
 : function_view_traits<Res (Args...)>
{
};

/* Const member functions.  function_view doesn't support these, but
  we need this in order to extract the type of function objects.
  Lambdas pass here, after starting at the operator() case,
  below.  */
template<typename Res, typename Class, typename... Args>
struct function_view_traits<Res (Class::*) (Args...) const>
 : function_view_traits<Res (Args...)>
{
};

/* Member functions.  Ditto, for function objects with non-const
  operator().  */
template<typename Res, typename Class, typename... Args>
struct function_view_traits<Res (Class::*) (Args...)>
 : function_view_traits<Res (Args...)>
{
};

/* Function objects, lambdas, std::function, any type that defines
  operator().  */
template<typename FuncObj>
struct function_view_traits
 : function_view_traits <decltype
                         (&std::remove_reference<FuncObj>::type::operator())>
{
};

} /* namespace fv_detail */

/* Make a function_view from a callable.  Useful to automatically
  deduce the function_view's template argument type.  */
template<typename Callable>
auto make_function_view (Callable &&callable)
 -> typename fv_detail::function_view_traits<Callable>::type
{
 using fv = typename fv_detail::function_view_traits<Callable>::type;
 return fv (std::forward<Callable> (callable));
}

} /* namespace gdb */

#endif /* GDBSUPPORT_FUNCTION_VIEW_H */