aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbsupport/function-view.h
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbsupport/function-view.h')
-rw-r--r--gdb/gdbsupport/function-view.h323
1 files changed, 0 insertions, 323 deletions
diff --git a/gdb/gdbsupport/function-view.h b/gdb/gdbsupport/function-view.h
deleted file mode 100644
index 4e205b8..0000000
--- a/gdb/gdbsupport/function-view.h
+++ /dev/null
@@ -1,323 +0,0 @@
-/* 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