/* 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 */