binutils-gdb/gdbsupport/function-view.h
Tom Tromey 01027315f5 Move gdbsupport to the top level
This patch moves the gdbsupport directory to the top level.  This is
the next step in the ongoing project to move gdbserver to the top
level.

The bulk of this patch was created by "git mv gdb/gdbsupport gdbsupport".

This patch then adds a build system to gdbsupport and wires it into
the top level.  Then it changes gdb to use the top-level build.

gdbserver, on the other hand, is not yet changed.  It still does its
own build of gdbsupport.

ChangeLog
2020-01-14  Tom Tromey  <tom@tromey.com>

	* src-release.sh (GDB_SUPPORT_DIRS): Add gdbsupport.
	* MAINTAINERS: Add gdbsupport.
	* configure: Rebuild.
	* configure.ac (configdirs): Add gdbsupport.
	* gdbsupport: New directory, move from gdb/gdbsupport.
	* Makefile.def (host_modules, dependencies): Add gnulib.
	* Makefile.in: Rebuild.

gdb/ChangeLog
2020-01-14  Tom Tromey  <tom@tromey.com>

	* nat/x86-linux-dregs.c: Include configh.h.
	* nat/linux-ptrace.c: Include configh.h.
	* nat/linux-btrace.c: Include configh.h.
	* defs.h: Include config.h, bfd.h.
	* configure.ac: Don't source common.host.
	(CONFIG_OBS, CONFIG_SRCS): Remove gdbsupport files.
	* configure: Rebuild.
	* acinclude.m4: Update path.
	* Makefile.in (SUPPORT, LIBSUPPORT, INCSUPPORT): New variables.
	(CONFIG_SRC_SUBDIR): Remove gdbsupport.
	(INTERNAL_CFLAGS_BASE): Add INCSUPPORT.
	(CLIBS): Add LIBSUPPORT.
	(CDEPS): Likewise.
	(COMMON_SFILES): Remove gdbsupport files.
	(HFILES_NO_SRCDIR): Likewise.
	(stamp-version): Update path to create-version.sh.
	(ALLDEPFILES): Remove gdbsupport files.

gdb/gdbserver/ChangeLog
2020-01-14  Tom Tromey  <tom@tromey.com>

	* server.h: Include config.h.
	* gdbreplay.c: Include config.h.
	* configure: Rebuild.
	* configure.ac: Don't source common.host.
	* acinclude.m4: Update path.
	* Makefile.in (INCSUPPORT): New variable.
	(INCLUDE_CFLAGS): Add INCSUPPORT.
	(SFILES): Update paths.
	(version-generated.c): Update path to create-version.sh.
	(gdbsupport/%-ipa.o, gdbsupport/%.o): Update paths.

gdbsupport/ChangeLog
2020-01-14  Tom Tromey  <tom@tromey.com>

	* common-defs.h: Add GDBSERVER case.  Update includes.
	* acinclude.m4, aclocal.m4, config.in, configure, configure.ac,
	Makefile.am, Makefile.in, README: New files.
	* Moved from ../gdb/gdbsupport/

Change-Id: I07632e7798635c1bab389bf885971e584fb4bb78
2020-01-14 16:25:02 -07:00

324 lines
11 KiB
C++

/* Copyright (C) 2017-2020 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);
You can find unit tests covering the whole API in
unittests/function-view-selftests.c. */
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::result_of<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 gdb */
#endif