mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
c95dd24e79
When building with Clang 14 (using gcc 12 libstdc++ headers), I get: CXX dwarf2/read.o In file included from /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:94: /home/simark/src/binutils-gdb/gdb/../gdbsupport/parallel-for.h:142:21: error: 'result_of<(lambda at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7124:5) (__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter> *, std::__cxx1998::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>>, std::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>, std::random_access_iterator_tag>, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter> *, std::__cxx1998::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>>, std::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>, std::random_access_iterator_tag>)>' is deprecated: use 'std::invoke_result' instead [-Werror,-Wdeprecated-declarations] = typename std::result_of<RangeFunction (RandomIt, RandomIt)>::type; ^ /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7122:14: note: in instantiation of function template specialization 'gdb::parallel_for_each<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter> *, std::__cxx1998::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>>, std::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>, std::random_access_iterator_tag>, (lambda at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7124:5)>' requested here = gdb::parallel_for_each (1, per_bfd->all_comp_units.begin (), ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.1.1/../../../../include/c++/12.1.1/type_traits:2597:9: note: 'result_of<(lambda at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7124:5) (__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter> *, std::__cxx1998::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>>, std::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>, std::random_access_iterator_tag>, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter> *, std::__cxx1998::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>>, std::vector<std::unique_ptr<dwarf2_per_cu_data, dwarf2_per_cu_data_deleter>>, std::random_access_iterator_tag>)>' has been explicitly marked deprecated here { } _GLIBCXX17_DEPRECATED_SUGGEST("std::invoke_result"); ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.1.1/../../../../include/c++/12.1.1/x86_64-pc-linux-gnu/bits/c++config.h:120:45: note: expanded from macro '_GLIBCXX17_DEPRECATED_SUGGEST' # define _GLIBCXX17_DEPRECATED_SUGGEST(ALT) _GLIBCXX_DEPRECATED_SUGGEST(ALT) ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.1.1/../../../../include/c++/12.1.1/x86_64-pc-linux-gnu/bits/c++config.h:96:19: note: expanded from macro '_GLIBCXX_DEPRECATED_SUGGEST' __attribute__ ((__deprecated__ ("use '" ALT "' instead"))) ^ It complains about the use of std::result_of, which is deprecated in C++17 and removed in C++20: https://en.cppreference.com/w/cpp/types/result_of Given we'll have to transition to std::invoke_result eventually, make a GDB wrapper to mimimc std::invoke_result, which uses std::invoke_result for C++ >= 17 and std::result_of otherwise. This way, it will be easy to remove the wrapper in the future, just replace gdb:: with std::. Tested by building with gcc 12 in -std=c++11 and -std=c++17 mode, and clang in -std=c++17 mode (I did not test fully with clang in -std=c++11 mode because there are other unrelated issues). Change-Id: I50debde0a3307a7bc67fcf8fceefda51860efc1d
452 lines
15 KiB
C++
452 lines
15 KiB
C++
/* Copyright (C) 2017-2022 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 "invoke-result.h"
|
|
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 gdb::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
|