aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2022-07-21 21:55:37 +0100
committerPedro Alves <pedro@palves.net>2022-07-22 14:18:49 +0100
commit37c2487312d2997bed16343f642ecb5e83bac979 (patch)
tree29d0d11be9ee7eff3e28246a14758db1a30cc758
parent6577f365ebdee7dda71cb996efa29d3714cbccd0 (diff)
downloadfsf-binutils-gdb-users/palves/make_function_view.zip
fsf-binutils-gdb-users/palves/make_function_view.tar.gz
fsf-binutils-gdb-users/palves/make_function_view.tar.bz2
Introduce gdb::make_function_viewusers/palves/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
-rw-r--r--gdb/unittests/function-view-selftests.c82
-rw-r--r--gdbsupport/function-view.h127
2 files changed, 208 insertions, 1 deletions
diff --git a/gdb/unittests/function-view-selftests.c b/gdb/unittests/function-view-selftests.c
index 7af0245..726c223 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 1875cd1..9f8a868 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