public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
* [binutils-gdb] Introduce gdb::make_function_view
@ 2022-08-05 14:13 Tom de Vries
  0 siblings, 0 replies; only message in thread
From: Tom de Vries @ 2022-08-05 14:13 UTC (permalink / raw)
  To: gdb-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=377c3a9c91785d5df5c0d96121160e6210204cc0

commit 377c3a9c91785d5df5c0d96121160e6210204cc0
Author: Pedro Alves <pedro@palves.net>
Date:   Fri Aug 5 16:12:56 2022 +0200

    Introduce gdb::make_function_view
    
    This adds gdb::make_function_view, which lets you create a function
    view from a callable without specifying the function_view's template
    parameter.  For example, this:
    
        auto lambda = [&] (int) { ... };
        auto fv = gdb::make_function_view (lambda);
    
    instead of:
    
        auto lambda = [&] (int) { ... };
        gdb::function_view<void (int)> fv = lambda;
    
    It is particularly useful if you have a template function with an
    optional function_view parameter, whose type depends on the function's
    template parameters.  Like:
    
        template<typename T>
        void my_function (T v, gdb::function_view<void(T)> callback = nullptr);
    
    For such a function, the type of the callback argument you pass must
    already be a function_view.  I.e., this wouldn't compile:
    
        auto lambda = [&] (int) { ... };
        my_function (1, lambda);
    
    With gdb::make_function_view, you can write the call like so:
    
        auto lambda = [&] (int) { ... };
        my_function (1, gdb::make_function_view (lambda));
    
    Unit tests included.
    
    Tested by building with GCC 9.4, Clang 10, and GCC 4.8.5, on x86_64
    GNU/Linux, and running the unit tests.
    
    Change-Id: I5c4b3b4455ed6f0d8878cf1be189bea3ee63f626

Diff:
---
 gdb/unittests/function-view-selftests.c |  82 ++++++++++++++++++++-
 gdbsupport/function-view.h              | 127 ++++++++++++++++++++++++++++++++
 2 files changed, 208 insertions(+), 1 deletion(-)

diff --git a/gdb/unittests/function-view-selftests.c b/gdb/unittests/function-view-selftests.c
index 7af0245c570..726c22323a8 100644
--- a/gdb/unittests/function-view-selftests.c
+++ b/gdb/unittests/function-view-selftests.c
@@ -61,7 +61,7 @@ struct plus_one_int_func_obj
 };
 
 static void
-run_tests ()
+test_function_view ()
 {
   /* A simple lambda.  */
   auto plus_one_lambda = [] (int val) { return ++val; };
@@ -168,6 +168,86 @@ run_tests ()
   SELF_CHECK (!check_op_eq_null);
 }
 
+/* A template function where the function_view type is dependent on a
+   template parameter.  */
+
+template<typename T>
+static int
+tmpl_func (T val, gdb::function_view<T (T)> callback)
+{
+  return callback (val) + 1;
+}
+
+static int
+make_fv_test_func (int val)
+{
+  return val + 1;
+}
+
+/* A function object with const operator().  */
+
+struct func_obj_const_op
+{
+  int operator() (int val) const
+  {
+    return val + 1;
+  }
+};
+
+/* A function object with non-const operator().  */
+
+struct func_obj_non_const_op
+{
+  int operator() (int val)
+  {
+    return val + 1;
+  }
+};
+
+static void
+test_make_function_view ()
+{
+  /* Function reference.  */
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (make_fv_test_func)));
+
+  /* Function pointer.  */
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (&make_fv_test_func)));
+
+  /* Reference to const and non-const function pointers.  */
+  typedef int (*func_ptr) (int);
+  func_ptr ptr = make_fv_test_func;
+  const func_ptr cptr = make_fv_test_func;
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (ptr)));
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (cptr)));
+
+  /* Lambdas.  */
+
+  auto lambda = [] (int val) -> int { return val + 1; };
+
+  /* This wouldn't compile, since tmpl_func is a template and its
+     function_view argument's callable type is a dependent type.  The
+     passed argument must be of the exact type of the function's
+     parameter.  */
+  // SELF_CHECK (3 == tmpl_func (1, lambda));
+
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (lambda)));
+
+  /* Regular function objects.  */
+
+  func_obj_non_const_op fobj;
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (fobj)));
+
+  func_obj_const_op cfobj;
+  SELF_CHECK (3 == tmpl_func (1, gdb::make_function_view (cfobj)));
+}
+
+static void
+run_tests ()
+{
+  test_function_view ();
+  test_make_function_view ();
+}
+
 } /* namespace function_view */
 } /* namespace selftests */
 
diff --git a/gdbsupport/function-view.h b/gdbsupport/function-view.h
index 1875cd104b7..9f8a8680cf4 100644
--- a/gdbsupport/function-view.h
+++ b/gdbsupport/function-view.h
@@ -148,6 +148,47 @@
 
     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.  */
 
@@ -318,6 +359,92 @@ 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


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-05 14:13 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-05 14:13 [binutils-gdb] Introduce gdb::make_function_view Tom de Vries

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).