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
This commit is contained in:
Pedro Alves
2022-08-05 16:12:56 +02:00
committed by Tom de Vries
parent 5ee285ca3e
commit 377c3a9c91
2 changed files with 208 additions and 1 deletions

View File

@@ -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