mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:54:41 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
452 lines
15 KiB
C++
452 lines
15 KiB
C++
/* Copyright (C) 2017-2024 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef COMMON_FUNCTION_VIEW_H
|
|
#define COMMON_FUNCTION_VIEW_H
|
|
|
|
/* function_view is a polymorphic type-erasing wrapper class that
|
|
encapsulates a non-owning reference to arbitrary callable objects.
|
|
|
|
A way to put it is that function_view is to std::function like
|
|
std::string_view is to std::string. While std::function stores a
|
|
type-erased callable object internally, function_view holds a
|
|
type-erased reference to an external callable object.
|
|
|
|
This is meant to be used as callback type of a function that:
|
|
|
|
#1 - Takes a callback as parameter.
|
|
|
|
#2 - Wants to support arbitrary callable objects as callback type
|
|
(e.g., stateful function objects, lambda closures, free
|
|
functions).
|
|
|
|
#3 - Does not store the callback anywhere; instead the function
|
|
just calls the callback directly or forwards it to some
|
|
other function that calls it.
|
|
|
|
#4 - Can't be, or we don't want it to be, a template function
|
|
with the callable type as template parameter. For example,
|
|
when the callback is a parameter of a virtual member
|
|
function, or when putting the function template in a header
|
|
would expose too much implementation detail.
|
|
|
|
Note that the C-style "function pointer" + "void *data" callback
|
|
parameter idiom fails requirement #2 above. Please don't add new
|
|
uses of that idiom. I.e., something like this wouldn't work;
|
|
|
|
typedef bool (iterate_over_foos_cb) (foo *f, void *user_data),
|
|
void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data);
|
|
|
|
foo *find_foo_by_type (int type)
|
|
{
|
|
foo *found = nullptr;
|
|
|
|
iterate_over_foos ([&] (foo *f, void *data)
|
|
{
|
|
if (foo->type == type)
|
|
{
|
|
found = foo;
|
|
return true; // stop iterating
|
|
}
|
|
return false; // continue iterating
|
|
}, NULL);
|
|
|
|
return found;
|
|
}
|
|
|
|
The above wouldn't compile, because lambdas with captures can't be
|
|
implicitly converted to a function pointer (because a capture means
|
|
some context data must be passed to the lambda somehow).
|
|
|
|
C++11 gave us std::function as type-erased wrapper around arbitrary
|
|
callables, however, std::function is not an ideal fit for transient
|
|
callbacks such as the use case above. For this use case, which is
|
|
quite pervasive, a function_view is a better choice, because while
|
|
function_view is light and does not require any heap allocation,
|
|
std::function is a heavy-weight object with value semantics that
|
|
generally requires a heap allocation on construction/assignment of
|
|
the target callable. In addition, while it is possible to use
|
|
std::function in such a way that avoids most of the overhead by
|
|
making sure to only construct it with callables of types that fit
|
|
std::function's small object optimization, such as function
|
|
pointers and std::reference_wrapper callables, that is quite
|
|
inconvenient in practice, because restricting to free-function
|
|
callables would imply no state/capture/closure, which we need in
|
|
most cases, and std::reference_wrapper implies remembering to use
|
|
std::ref/std::cref where the callable is constructed, with the
|
|
added inconvenience that std::ref/std::cref have deleted rvalue-ref
|
|
overloads, meaning you can't use unnamed/temporary lambdas with
|
|
them.
|
|
|
|
Note that because function_view is a non-owning view of a callable,
|
|
care must be taken to ensure that the callable outlives the
|
|
function_view that calls it. This is not really a problem for the
|
|
use case function_view is intended for, such as passing a temporary
|
|
function object / lambda to a function that accepts a callback,
|
|
because in those cases, the temporary is guaranteed to be live
|
|
until the called function returns.
|
|
|
|
Calling a function_view with no associated target is undefined,
|
|
unlike with std::function, which throws std::bad_function_call.
|
|
This is by design, to avoid the otherwise necessary NULL check in
|
|
function_view::operator().
|
|
|
|
Since function_view objects are small (a pair of pointers), they
|
|
should generally be passed around by value.
|
|
|
|
Usage:
|
|
|
|
Given this function that accepts a callback:
|
|
|
|
void
|
|
iterate_over_foos (gdb::function_view<void (foo *)> callback)
|
|
{
|
|
for (auto &foo : foos)
|
|
callback (&foo);
|
|
}
|
|
|
|
you can call it like this, passing a lambda as callback:
|
|
|
|
iterate_over_foos ([&] (foo *f)
|
|
{
|
|
process_one_foo (f);
|
|
});
|
|
|
|
or like this, passing a function object as callback:
|
|
|
|
struct function_object
|
|
{
|
|
void operator() (foo *f)
|
|
{
|
|
if (s->check ())
|
|
process_one_foo (f);
|
|
}
|
|
|
|
// some state
|
|
state *s;
|
|
};
|
|
|
|
state mystate;
|
|
function_object matcher {&mystate};
|
|
iterate_over_foos (matcher);
|
|
|
|
or like this, passing a function pointer as callback:
|
|
|
|
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. */
|
|
|
|
#include <type_traits>
|
|
namespace gdb {
|
|
|
|
namespace fv_detail {
|
|
/* Bits shared by all function_view instantiations that do not depend
|
|
on the template parameters. */
|
|
|
|
/* Storage for the erased callable. This is a union in order to be
|
|
able to save both a function object (data) pointer or a function
|
|
pointer without triggering undefined behavior. */
|
|
union erased_callable
|
|
{
|
|
/* For function objects. */
|
|
void *data;
|
|
|
|
/* For function pointers. */
|
|
void (*fn) ();
|
|
};
|
|
|
|
} /* namespace fv_detail */
|
|
|
|
/* Use partial specialization to get access to the callable's
|
|
signature. */
|
|
template<class Signature>
|
|
struct function_view;
|
|
|
|
template<typename Res, typename... Args>
|
|
class function_view<Res (Args...)>
|
|
{
|
|
template<typename From, typename To>
|
|
using CompatibleReturnType
|
|
= Or<std::is_void<To>,
|
|
std::is_same<From, To>,
|
|
std::is_convertible<From, To>>;
|
|
|
|
/* True if Func can be called with Args, and either the result is
|
|
Res, convertible to Res or Res is void. */
|
|
template<typename Callable,
|
|
typename Res2 = typename std::invoke_result<Callable &, Args...>::type>
|
|
struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
|
|
{};
|
|
|
|
/* True if Callable is a function_view. Used to avoid hijacking the
|
|
copy ctor. */
|
|
template <typename Callable>
|
|
struct IsFunctionView
|
|
: std::is_same<function_view, typename std::decay<Callable>::type>
|
|
{};
|
|
|
|
public:
|
|
|
|
/* NULL by default. */
|
|
constexpr function_view () noexcept
|
|
: m_erased_callable {},
|
|
m_invoker {}
|
|
{}
|
|
|
|
/* Default copy/assignment is fine. */
|
|
function_view (const function_view &) = default;
|
|
function_view &operator= (const function_view &) = default;
|
|
|
|
/* This is the main entry point. Use SFINAE to avoid hijacking the
|
|
copy constructor and to ensure that the target type is
|
|
compatible. */
|
|
template
|
|
<typename Callable,
|
|
typename = Requires<Not<IsFunctionView<Callable>>>,
|
|
typename = Requires<IsCompatibleCallable<Callable>>>
|
|
function_view (Callable &&callable) noexcept
|
|
{
|
|
bind (callable);
|
|
}
|
|
|
|
/* Construct a NULL function_view. */
|
|
constexpr function_view (std::nullptr_t) noexcept
|
|
: m_erased_callable {},
|
|
m_invoker {}
|
|
{}
|
|
|
|
/* Clear a function_view. */
|
|
function_view &operator= (std::nullptr_t) noexcept
|
|
{
|
|
m_invoker = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
/* Return true if the wrapper has a target, false otherwise. Note
|
|
we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
|
|
know which member of the union is active right now. */
|
|
constexpr explicit operator bool () const noexcept
|
|
{ return m_invoker != nullptr; }
|
|
|
|
/* Call the callable. */
|
|
Res operator () (Args... args) const
|
|
{ return m_invoker (m_erased_callable, std::forward<Args> (args)...); }
|
|
|
|
private:
|
|
|
|
/* Bind this function_view to a compatible function object
|
|
reference. */
|
|
template <typename Callable>
|
|
void bind (Callable &callable) noexcept
|
|
{
|
|
m_erased_callable.data = (void *) std::addressof (callable);
|
|
m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
|
|
noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
|
|
{
|
|
auto &restored_callable = *static_cast<Callable *> (ecall.data);
|
|
/* The explicit cast to Res avoids a compile error when Res is
|
|
void and the callable returns non-void. */
|
|
return (Res) restored_callable (std::forward<Args> (args)...);
|
|
};
|
|
}
|
|
|
|
/* Bind this function_view to a compatible function pointer.
|
|
|
|
Making this a separate function allows avoiding one indirection,
|
|
by storing the function pointer directly in the storage, instead
|
|
of a pointer to pointer. erased_callable is then a union in
|
|
order to avoid storing a function pointer as a data pointer here,
|
|
which would be undefined. */
|
|
template<class Res2, typename... Args2>
|
|
void bind (Res2 (*fn) (Args2...)) noexcept
|
|
{
|
|
m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
|
|
m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
|
|
noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
|
|
{
|
|
auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
|
|
/* The explicit cast to Res avoids a compile error when Res is
|
|
void and the callable returns non-void. */
|
|
return (Res) restored_fn (std::forward<Args> (args)...);
|
|
};
|
|
}
|
|
|
|
/* Storage for the erased callable. */
|
|
fv_detail::erased_callable m_erased_callable;
|
|
|
|
/* The invoker. This is set to a capture-less lambda by one of the
|
|
'bind' overloads. The lambda restores the right type of the
|
|
callable (which is passed as first argument), and forwards the
|
|
args. */
|
|
Res (*m_invoker) (fv_detail::erased_callable, Args...);
|
|
};
|
|
|
|
/* Allow comparison with NULL. Defer the work to the in-class
|
|
operator bool implementation. */
|
|
|
|
template<typename Res, typename... Args>
|
|
constexpr inline bool
|
|
operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
|
|
{ return !static_cast<bool> (f); }
|
|
|
|
template<typename Res, typename... Args>
|
|
constexpr inline bool
|
|
operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
|
|
{ return !static_cast<bool> (f); }
|
|
|
|
template<typename Res, typename... Args>
|
|
constexpr inline bool
|
|
operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
|
|
{ return static_cast<bool> (f); }
|
|
|
|
template<typename Res, typename... Args>
|
|
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
|