[v1.1,1/3] Introduce gdb::function_view

Message ID 20baebcd-0f6a-89ca-ef34-503795171d43@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves Feb. 22, 2017, 5:49 p.m. UTC
  On 02/22/2017 05:40 PM, Pedro Alves wrote:

> I'll send the updated (squashed) patch as a reply.

Here is is.  Thanks for the quick review!

From 997f0e94cbd2d1cdb4bf7612df1de72ce1a6eb56 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 22 Feb 2017 17:43:09 +0000
Subject: [PATCH] Introduce gdb::function_view

This commit adds new function_view type.  This type holds a a
non-owning reference to a callable.  It is meant to be used as
callback type of functions, instead of using C-style pair of function
pointer and 'void *data' arguments.  function_view allows passing
references to stateful function objects / lambdas w/ captures as
callbacks efficiently, while function pointer + 'void *' does not.

See the intro in the new function-view.h header for more.

Unit tests included.  I added a new gdb/unittests/ subdir this time,
instead of putting the tests under gdb/.  If this is agreed to be a
good idea, some of the current selftests that exercise gdb/common/
things but live in gdb/ could move here (e.g., gdb/utils-selftests.c).

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS, SUBDIR_UNITTESTS_OBS): New.
	(SFILES): Add $(SUBDIR_UNITTEST_SRCS).
	(COMMON_OBS): Add $(SUBDIR_UNITTEST_OBS).
	(%.o) <unittests/%.c>: New pattern.
	(INIT_FILES): Add $(SUBDIR_UNITTESTS_SRCS).
	* common/function-view.h: New file.
	* unittests/function-view-selftests.c: New file.
---
 gdb/Makefile.in                         |  24 ++-
 gdb/common/function-view.h              | 355 ++++++++++++++++++++++++++++++++
 gdb/unittests/function-view-selftests.c | 184 +++++++++++++++++
 3 files changed, 560 insertions(+), 3 deletions(-)
 create mode 100644 gdb/common/function-view.h
 create mode 100644 gdb/unittests/function-view-selftests.c
  

Comments

Yao Qi Feb. 22, 2017, 10:11 p.m. UTC | #1
On 17-02-22 17:49:22, Pedro Alves wrote:
> On 02/22/2017 05:40 PM, Pedro Alves wrote:
> 
> > I'll send the updated (squashed) patch as a reply.
> 
> Here is is.  Thanks for the quick review!
> 
> From 997f0e94cbd2d1cdb4bf7612df1de72ce1a6eb56 Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 22 Feb 2017 17:43:09 +0000
> Subject: [PATCH] Introduce gdb::function_view
> 
> This commit adds new function_view type.  This type holds a a

Double "a" at the end.

> non-owning reference to a callable.  It is meant to be used as
> callback type of functions, instead of using C-style pair of function
> pointer and 'void *data' arguments.  function_view allows passing
> references to stateful function objects / lambdas w/ captures as
> callbacks efficiently, while function pointer + 'void *' does not.
> 
> See the intro in the new function-view.h header for more.
> 
> Unit tests included.  I added a new gdb/unittests/ subdir this time,
> instead of putting the tests under gdb/.  If this is agreed to be a
> good idea, some of the current selftests that exercise gdb/common/
> things but live in gdb/ could move here (e.g., gdb/utils-selftests.c).

I wanted to add gdb/unittests for a while, but didn't have a chance to
do so.  Yes, it is a good idea to me.  How is GDB unit tests, like
disasm-selftests.c?  Do you want to move it to gdb/unittests/?

> +   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
> +   while function_view is light and does not require any heap

Double "while"s.

> +namespace gdb {
> +

This is a new namespace in gdb source.  What is the rule of using this
namespace?
  
Yao Qi Feb. 22, 2017, 10:23 p.m. UTC | #2
On 17-02-22 17:49:22, Pedro Alves wrote:
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 43253d3..a4cac36 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -523,6 +523,12 @@ SUBDIR_PYTHON_DEPS =
>  SUBDIR_PYTHON_LDFLAGS =
>  SUBDIR_PYTHON_CFLAGS =
>  
> +SUBDIR_UNITTESTS_SRCS = \
> +	unittests/function-view-selftests.c
> +
> +SUBDIR_UNITTESTS_OBS = \
> +	function-view-selftests.o
> +
>  # Opcodes currently live in one of two places.  Either they are in the
>  # opcode library, typically ../opcodes, or they are in a header file
>  # in INCLUDE_DIR.
> @@ -1216,7 +1222,8 @@ SFILES = \
>  	common/xml-utils.c \
>  	mi/mi-common.c \
>  	target/waitstatus.c \
> -	$(SUBDIR_GCC_COMPILE_SRCS)
> +	$(SUBDIR_GCC_COMPILE_SRCS) \
> +	$(SUBDIR_UNITTEST_SRCS)
>  
>  LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
>  
> @@ -1800,7 +1807,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
>  	xml-syscall.o \
>  	xml-tdesc.o \
>  	xml-utils.o \
> -	$(SUBDIR_GCC_COMPILE_OBS)
> +	$(SUBDIR_GCC_COMPILE_OBS) \
> +	$(SUBDIR_UNITTESTS_OBS)
>

Can we add SUBDIR_UNITESTS_OBS to CONFIG_OBS (in configure.ac) if
GDB_SELF_TEST is defined?

>  TSOBS = inflow.o
>  
> @@ -1909,6 +1917,10 @@ all: gdb$(EXEEXT) $(CONFIG_ALL)
>  	$(COMPILE) $<
>  	$(POSTCOMPILE)
>  
> +%.o: ${srcdir}/unittests/%.c
> +	$(COMPILE) $<
> +	$(POSTCOMPILE)
> +
>  # Specify an explicit rule for gdb/common/agent.c, to avoid a clash with the
>  # object file generate by gdb/agent.c.
>  common-agent.o: $(srcdir)/common/agent.c
> @@ -2124,7 +2136,13 @@ test-cp-name-parser$(EXEEXT): test-cp-name-parser.o $(LIBIBERTY)
>  # duplicates.  Files in the gdb/ directory can end up appearing in
>  # COMMON_OBS (as a .o file) and CONFIG_SRCS (as a .c file).
>  
> -INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS) $(SUBDIR_GCC_COMPILE_SRCS)
> +INIT_FILES = \
> +	$(COMMON_OBS) \
> +	$(TSOBS) \
> +	$(CONFIG_SRCS) \
> +	$(SUBDIR_GCC_COMPILE_SRCS) \
> +	$(SUBDIR_UNITTESTS_SRCS)
> +

If so, SUBDIR_UNITESTS_SRCS is added to CONFIG_SRCS, we don't need this
change.

> diff --git a/gdb/unittests/function-view-selftests.c b/gdb/unittests/function-view-selftests.c
> new file mode 100644
> index 0000000..3e5369b
> --- /dev/null
> +++ b/gdb/unittests/function-view-selftests.c
> @@ -0,0 +1,184 @@
> +/* Self tests for function_view for GDB, the GNU debugger.
> +
> +   Copyright (C) 2017 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/>.  */
> +
> +#include "defs.h"
> +#include "selftest.h"
> +#include "common/function-view.h"
> +
> +#if GDB_SELF_TEST

If gdb/unittests/*.c are compiled when GDB_SELF_TEST is defined, we
don't need to check GDB_SELF_TEST here.

> +
> +namespace selftests {
> +namespace function_view {
> +

> +
> +} /* namespace function_view */
> +} /* namespace selftests */
> +
> +#endif
> +
> +void
> +_initialize_function_view_selftests ()
> +{
> +#if GDB_SELF_TEST

This is not needed as well.

> +  register_self_test (selftests::function_view::run_tests);
> +#endif
> +}
> -- 
> 2.5.5
> 
>
  
Pedro Alves Feb. 23, 2017, 2:49 p.m. UTC | #3
On 02/22/2017 10:11 PM, Yao Qi wrote:

> Double "a" at the end.

> 
> Double "while"s.

Thanks, fixed.

> 
>> +namespace gdb {
>> +
> 
> This is a new namespace in gdb source.  What is the rule of using this
> namespace?

I don't have a hard rule predetermined.  I originally put the C++03
unique_ptr shim under the gdb namespace to make it clear at the
call sites that that was not the std type, but gdb's replacement.
I.e., gdb::unique_ptr vs std::unique_ptr.  Since then, gdb::unique_ptr is
gone, but gdb::unique_xmalloc_ptr remains, and we've been putting
these generic library-like utilities under the namespace:

 common/function-view.h:154:namespace gdb {
 common/function-view.h:353:} /* namespace gdb */
 common/gdb_unlinker.h:23:namespace gdb
 common/gdb_unique_ptr.h:25:namespace gdb
 common/gdb_unique_ptr.h:43:} /* namespace gdb */
 common/gdb_optional.h:23:namespace gdb
 common/gdb_ref_ptr.h:25:namespace gdb

I think putting these new things in _some_ namespace is the
right thing to do.  gdb is just the no-brainer namespace name.

IMO, _all_ of GDB should be under "namespace gdb".  Then these utilities
would either be put in "namespace gdb" too, or in a "namespace gtl",
for "gdb template library" or something like that.  Or we could
put them under "gtl" already.

I have a series that put the whole of gdb under namespace gdb:
 https://github.com/palves/gdb/commits/palves/cxx-gdb-namespace

I wrote that while experimenting with ways to use
gnulib's C++ namespace mode, in order to avoid the 
"#define open rpl_open" defines gnulib does...  I nowadays
believe the put-gdb-in-a-namespace approach is the best one.

However, when we put gdb under a namespace, debugging GDB itself
becomes a bit harder, since you then have to do "b gdb::some_function"
instead of "b some_function".  This is just dogfooding pain,
of course, users run into this too...

That's what led to me to work on this branch:

 https://github.com/palves/gdb/commits/palves/cp-linespec-2

with that, "b some_function" sets breakpoints on all
functions/methods named some_function in all namespaces.

And it was testing that branch with --target_board=dwarf4-gdb-index
that led to the bug fixed by this series...  It's a never
ending story... :-)

Thanks,
Pedro Alves
  
Yao Qi Feb. 23, 2017, 3:11 p.m. UTC | #4
Pedro Alves <palves@redhat.com> writes:

> I think putting these new things in _some_ namespace is the
> right thing to do.  gdb is just the no-brainer namespace name.

Yes, that right.

>
> IMO, _all_ of GDB should be under "namespace gdb".  Then these utilities
> would either be put in "namespace gdb" too, or in a "namespace gtl",
> for "gdb template library" or something like that.  Or we could
> put them under "gtl" already.

IMO, "gdb template library" or "gtl" is project name, but it doesn't fit
well as a namespace name.  I like "gdb::utils" :).
  
Pedro Alves Feb. 23, 2017, 3:20 p.m. UTC | #5
On 02/23/2017 03:11 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> I think putting these new things in _some_ namespace is the
>> right thing to do.  gdb is just the no-brainer namespace name.
> 
> Yes, that right.
> 
>>
>> IMO, _all_ of GDB should be under "namespace gdb".  Then these utilities
>> would either be put in "namespace gdb" too, or in a "namespace gtl",
>> for "gdb template library" or something like that.  Or we could
>> put them under "gtl" already.
> 
> IMO, "gdb template library" or "gtl" is project name, but it doesn't fit
> well as a namespace name.  I like "gdb::utils" :).

"gdb::utils::function_view" is maybe a bit too long for code
that is going to end up used a lot, though.

"gdb::function_view" or "gtl::function_view" also has the "advantage"
that replacing "gdb" with "std" at some point does not require
reindenting the code.  (e.g., gdb::optional -> std::optional when
we get to C++17).  1/2 :-)

I think the easiest is to keep using "gdb" for now, and revisit
if/when we move everything under gdb.

Thanks,
Pedro Alves
  
Yao Qi Feb. 23, 2017, 3:34 p.m. UTC | #6
On Thu, Feb 23, 2017 at 3:20 PM, Pedro Alves <palves@redhat.com> wrote:
>
> I think the easiest is to keep using "gdb" for now, and revisit
> if/when we move everything under gdb.
>

That is fine by me.
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 43253d3..a4cac36 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -523,6 +523,12 @@  SUBDIR_PYTHON_DEPS =
 SUBDIR_PYTHON_LDFLAGS =
 SUBDIR_PYTHON_CFLAGS =
 
+SUBDIR_UNITTESTS_SRCS = \
+	unittests/function-view-selftests.c
+
+SUBDIR_UNITTESTS_OBS = \
+	function-view-selftests.o
+
 # Opcodes currently live in one of two places.  Either they are in the
 # opcode library, typically ../opcodes, or they are in a header file
 # in INCLUDE_DIR.
@@ -1216,7 +1222,8 @@  SFILES = \
 	common/xml-utils.c \
 	mi/mi-common.c \
 	target/waitstatus.c \
-	$(SUBDIR_GCC_COMPILE_SRCS)
+	$(SUBDIR_GCC_COMPILE_SRCS) \
+	$(SUBDIR_UNITTEST_SRCS)
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
 
@@ -1800,7 +1807,8 @@  COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	xml-syscall.o \
 	xml-tdesc.o \
 	xml-utils.o \
-	$(SUBDIR_GCC_COMPILE_OBS)
+	$(SUBDIR_GCC_COMPILE_OBS) \
+	$(SUBDIR_UNITTESTS_OBS)
 
 TSOBS = inflow.o
 
@@ -1909,6 +1917,10 @@  all: gdb$(EXEEXT) $(CONFIG_ALL)
 	$(COMPILE) $<
 	$(POSTCOMPILE)
 
+%.o: ${srcdir}/unittests/%.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
+
 # Specify an explicit rule for gdb/common/agent.c, to avoid a clash with the
 # object file generate by gdb/agent.c.
 common-agent.o: $(srcdir)/common/agent.c
@@ -2124,7 +2136,13 @@  test-cp-name-parser$(EXEEXT): test-cp-name-parser.o $(LIBIBERTY)
 # duplicates.  Files in the gdb/ directory can end up appearing in
 # COMMON_OBS (as a .o file) and CONFIG_SRCS (as a .c file).
 
-INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS) $(SUBDIR_GCC_COMPILE_SRCS)
+INIT_FILES = \
+	$(COMMON_OBS) \
+	$(TSOBS) \
+	$(CONFIG_SRCS) \
+	$(SUBDIR_GCC_COMPILE_SRCS) \
+	$(SUBDIR_UNITTESTS_SRCS)
+
 init.c: $(INIT_FILES)
 	@echo Making init.c
 	@rm -f init.c-tmp init.l-tmp
diff --git a/gdb/common/function-view.h b/gdb/common/function-view.h
new file mode 100644
index 0000000..af2593f
--- /dev/null
+++ b/gdb/common/function-view.h
@@ -0,0 +1,355 @@ 
+/* Copyright (C) 2017 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
+   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 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 traits {
+  /* A few trait helpers.  */
+  template<typename Predicate>
+  struct Not : public std::integral_constant<bool, !Predicate::value>
+  {};
+
+  template<typename...>
+  struct Or;
+
+  template<>
+  struct Or<> : public std::false_type
+  {};
+
+  template<typename B1>
+  struct Or<B1> : public B1
+  {};
+
+  template<typename B1, typename B2>
+  struct Or<B1, B2>
+    : public std::conditional<B1::value, B1, B2>::type
+  {};
+
+  template<typename B1,typename B2,typename B3, typename... Bn>
+  struct Or<B1, B2, B3, Bn...>
+    : public std::conditional<B1::value, B1, Or<B2, B3, Bn...>>::type
+  {};
+} /* namespace traits */
+
+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
+    = traits::Or<std::is_void<To>,
+		 std::is_same<From, To>,
+		 std::is_convertible<From, To>>;
+
+  /* True if Func can be called with Args, and the result, and the
+     result is convertible to Res, unless 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>
+  {};
+
+  /* Helper to make SFINAE logic easier to read.  */
+  template<typename Condition>
+  using Requires = typename std::enable_if<Condition::value, void>::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<traits::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
diff --git a/gdb/unittests/function-view-selftests.c b/gdb/unittests/function-view-selftests.c
new file mode 100644
index 0000000..3e5369b
--- /dev/null
+++ b/gdb/unittests/function-view-selftests.c
@@ -0,0 +1,184 @@ 
+/* Self tests for function_view for GDB, the GNU debugger.
+
+   Copyright (C) 2017 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/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "common/function-view.h"
+
+#if GDB_SELF_TEST
+
+namespace selftests {
+namespace function_view {
+
+static int
+plus_one_fn_int (int val)
+{
+  return ++val;
+}
+
+static short
+plus_one_fn_short (short val)
+{
+  return ++val;
+}
+
+static int
+call_callback_int (int val, gdb::function_view <int (int)> callback)
+{
+  return callback (val);
+}
+
+static void
+call_callback_void (int val, gdb::function_view <void (int)> callback)
+{
+  callback (val);
+}
+
+struct plus_one_int_func_obj
+{
+  int operator () (int val)
+  {
+    ++call_count;
+    return ++val;
+  }
+
+  /* Number of times called.  */
+  int call_count = 0;
+};
+
+static void
+run_tests ()
+{
+  /* A simple lambda.  */
+  auto plus_one_lambda = [] (int val) { return ++val; };
+
+  /* A function_view that references the lambda.  */
+  gdb::function_view<int (int)> plus_one_func_view (plus_one_lambda);
+
+  /* Check calling the lambda directly.  */
+  SELF_CHECK (plus_one_lambda (0) == 1);
+  SELF_CHECK (plus_one_lambda (1) == 2);
+
+  /* Check calling lambda via the view.  */
+  SELF_CHECK (plus_one_func_view (2) == 3);
+  SELF_CHECK (plus_one_func_view (3) == 4);
+
+  /* Check calling a function that takes a function_view as argument,
+     by value.  Pass a lambda, making sure a function_view is properly
+     constructed implicitly.  */
+  SELF_CHECK (call_callback_int (1, [] (int val)
+    {
+      return val + 2;
+    }) == 3);
+
+  /* Same, passing a named/lvalue lambda.  */
+  SELF_CHECK (call_callback_int (1, plus_one_lambda) == 2);
+  /* Same, passing named/lvalue function_view (should copy).  */
+  SELF_CHECK (call_callback_int (1, plus_one_func_view) == 2);
+
+  /* Check constructing a function view over a function-object
+     callable, and calling it.  */
+  plus_one_int_func_obj func_obj;
+  SELF_CHECK (func_obj (0) == 1);
+  SELF_CHECK (call_callback_int (1, func_obj) == 2);
+  /* Check that the callable was referenced, not copied.  */
+  SELF_CHECK (func_obj.call_count == 2);
+
+  /* Check constructing a function_view over a free-function callable,
+     and calling it.  */
+  SELF_CHECK (call_callback_int (1, plus_one_fn_int) == 2);
+
+  /* Check calling a function with a
+     compatible-but-not-exactly-the-same prototype.  */
+  SELF_CHECK (call_callback_int (1, [] (short val) -> short
+    {
+      return val + 2;
+    }) == 3);
+  /* Same, but passing a function pointer.  */
+  SELF_CHECK (call_callback_int (1, plus_one_fn_short) == 2);
+
+  /* Like std::function, a function_view that expects a void return
+     can reference callables with non-void return type.  The result is
+     simply discarded.  Check a lambda, function object and a function
+     pointer.  */
+  call_callback_void (1, [] (int val) -> int
+    {
+      return val + 2;
+    });
+  call_callback_void (1, func_obj);
+  call_callback_void (1, plus_one_fn_int);
+
+  /* Check that the main ctor doesn't hijack the copy ctor.  */
+  auto plus_one_func_view2 (plus_one_func_view);
+  auto plus_one_func_view3 (plus_one_func_view2);
+  static_assert (std::is_same<decltype (plus_one_func_view),
+		 decltype (plus_one_func_view2)>::value, "");
+  static_assert (std::is_same<decltype (plus_one_func_view),
+		 decltype (plus_one_func_view3)>::value, "");
+
+  SELF_CHECK (plus_one_func_view3 (1) == 2);
+
+  /* Likewise, but propagate a NULL callable.  If this calls the main
+     function_view ctor instead of the copy ctor by mistake, then
+     null_func_2 ends up non-NULL (because it'd instead reference
+     null_func_1 as just another callable).  */
+  constexpr gdb::function_view<int (int)> null_func_view_1 = nullptr;
+  constexpr auto null_func_view_2 (null_func_view_1);
+
+  /* While at it, check whether the function_view is bound using
+     various forms, op==, op!= and op bool.  */
+
+  /* op== */
+  static_assert (null_func_view_2 == nullptr, "");
+  static_assert (nullptr == null_func_view_2, "");
+  static_assert (null_func_view_2 == NULL, "");
+  static_assert (NULL == null_func_view_2, "");
+
+  /* op!= */
+  static_assert (!(null_func_view_2 != nullptr), "");
+  static_assert (!(nullptr != null_func_view_2), "");
+  static_assert (!(null_func_view_2 != NULL), "");
+  static_assert (!(NULL != null_func_view_2), "");
+
+  /* op bool */
+  static_assert (!null_func_view_2, "");
+
+  /* Check the nullptr_t ctor.  */
+  constexpr gdb::function_view<int (int)> check_ctor_nullptr (nullptr);
+  static_assert (!check_ctor_nullptr, "");
+
+  /* Check the nullptr_t op= */
+  gdb::function_view<int (int)> check_op_eq_null (plus_one_fn_int);
+  SELF_CHECK (check_op_eq_null);
+  check_op_eq_null = nullptr;
+  SELF_CHECK (!check_op_eq_null);
+}
+
+} /* namespace function_view */
+} /* namespace selftests */
+
+#endif
+
+void
+_initialize_function_view_selftests ()
+{
+#if GDB_SELF_TEST
+  register_self_test (selftests::function_view::run_tests);
+#endif
+}