diff options
author | Pedro Alves <pedro@palves.net> | 2022-07-21 21:55:37 +0100 |
---|---|---|
committer | Pedro Alves <pedro@palves.net> | 2022-07-22 14:18:49 +0100 |
commit | 37c2487312d2997bed16343f642ecb5e83bac979 (patch) | |
tree | 29d0d11be9ee7eff3e28246a14758db1a30cc758 | |
parent | 6577f365ebdee7dda71cb996efa29d3714cbccd0 (diff) | |
download | fsf-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.c | 82 | ||||
-rw-r--r-- | gdbsupport/function-view.h | 127 |
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 |