Compare commits

...

1 Commits

Author SHA1 Message Date
Pedro Alves
37c2487312 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
2022-07-22 14:18:49 +01:00
2 changed files with 208 additions and 1 deletions

View File

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

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