Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Simon Marchi <simon.marchi@polymtl.ca>
To: Pedro Alves <palves@redhat.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH 1/3] Introduce gdb::function_view
Date: Wed, 22 Feb 2017 18:02:00 -0000	[thread overview]
Message-ID: <61b1e228e36f3fbb0d8bd07432622e45@polymtl.ca> (raw)
In-Reply-To: <4c8528d8-0115-3bf3-0a16-42f44be580a9@redhat.com>

On 2017-02-22 12:40, Pedro Alves wrote:
> On 02/22/2017 03:15 PM, Simon Marchi wrote:
>>> +
>>> +   This is meant to be used as callback type of a function that:
>>> +
>>> +     #1 - Takes a callback as parameter.
>>> +
>>> +     #2 - Does not store the callback anywhere; instead if just 
>>> calls
>> 
>> if -> it
>> 
>>> +          it or forwards it to some other function that calls it.
> 
> Thanks.  Maybe that's too many confusing "it"s.  I've rewritten it to
> 
>      #2 - Does not store the callback anywhere; instead the function
>           just calls the callback directly or forwards it to some
>           other function that calls it.

LGTM.

>>> +
>>> +   Usage:
>>> +
>>> +   Given this function that accepts a callback:
>> 
>> It's not necessary, but it would be nice to have the equivalent 
>> example
>> of how it would've been done before (with a function pointer) so that
>> people can relate the following example to something they already 
>> know.
> 
> I wasn't sure whether that should be here, thinking it might
> be more appropriate for the internals manual?  I would paste
> an example using a callable as template parameter there too, for
> example.  But I guess writing it too can't hurt, since that's likely
> where people will look first when they want to know what are those
> "function_view"s in the source.
> 
> I've extended this section in that direction, and tried to clarify
> other bits along with it.  See below.
> 
>> 
>>> +    void
>>> +    iterate_over_foos (gdb::function_view<void (foo *)> callback)
>>> +    {
>>> +       for (auto & : foos)
>> 
>> I think you're missing a "foo" here.
> 
> Indeed.  Found another buglet in the examples that I fixed too.
> 
>>> +namespace traits
>>> +{
>> 
>> Is it intended to have this { on a separate line, unlike the other
>> namespace declarations?
> 
> Nope, somehow missed it.
> 
>> I am not going to try to understand any of this...  but as long as it
>> works I'm happy.
> 
> Yeah, these library facilities tend to require use of C++ that is
> not normally used by "regular" code.  In isolation, none of the
> features used is really complicated, but when they're all combined,
> it gives the impression otherwise.  The advantage is that it
> ends up all contained in a single place, while users of the library
> end up being simple and efficient.  Let me know if I can explain
> things better.
> 
> Here's the tweak I mean to squash down addressing your review.
> I also realized that the unit test had some formatting issues, and
> the symbol names uses there could be renamed to make things a bit
> clearer.
> 
> I'll send the updated (squashed) patch as a reply.
> 
> From 79ce1e9b5fc1e69747d033fc384c7f50c61be1ba Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 22 Feb 2017 17:30:00 +0000
> Subject: [PATCH] review
> 
> ---
>  gdb/common/function-view.h              | 104 
> ++++++++++++++++++++----------
>  gdb/unittests/function-view-selftests.c | 109 
> ++++++++++++++++----------------
>  2 files changed, 125 insertions(+), 88 deletions(-)
> 
> diff --git a/gdb/common/function-view.h b/gdb/common/function-view.h
> index 3303783..af2593f 100644
> --- a/gdb/common/function-view.h
> +++ b/gdb/common/function-view.h
> @@ -30,33 +30,67 @@
> 
>       #1 - Takes a callback as parameter.
> 
> -     #2 - Does not store the callback anywhere; instead if just calls
> -          it or forwards it to some other function that calls it.
> -
> -     #3 - When we don't want, or can't make said function 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.
> -
> -   For this use case, which is quite pervasive, a function_view is a
> -   better choice for callback type than std::function.  It is a better
> -   choice because std::function is a heavy-weight object with value
> +     #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, while function_view
> -   is light and does not require any heap allocation.  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, however,
> -   that is quite inconvenient in practice, because restricting to
> -   free-function callables would imply no state/capture (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 those function have deleted rvalue-ref
> -   overloads, meaning you can't use unnamed/temporary lambdas with
> -   them.
> +   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
> @@ -81,15 +115,16 @@
>      void
>      iterate_over_foos (gdb::function_view<void (foo *)> callback)
>      {
> -       for (auto & : foos)
> -         callback (&foo);
> +       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);
> -    });
> +    iterate_over_foos ([&] (foo *f)
> +      {
> +        process_one_foo (f);
> +      });
> 
>     or like this, passing a function object as callback:
> 
> @@ -97,15 +132,16 @@
>      {
>        void operator() (foo *f)
>        {
> -        if (s->check ())
> -          process_one_foo (f);
> +	if (s->check ())
> +	  process_one_foo (f);
>        }
> 
>        // some state
>        state *s;
>      };
> 
> -    function_object matcher (mystate);
> +    state mystate;
> +    function_object matcher {&mystate};
>      iterate_over_foos (matcher);
> 
>    or like this, passing a function pointer as callback:
> diff --git a/gdb/unittests/function-view-selftests.c
> b/gdb/unittests/function-view-selftests.c
> index 8f73bc4..3e5369b 100644
> --- a/gdb/unittests/function-view-selftests.c
> +++ b/gdb/unittests/function-view-selftests.c
> @@ -27,143 +27,144 @@ namespace selftests {
>  namespace function_view {
> 
>  static int
> -add_one (int count)
> +plus_one_fn_int (int val)
>  {
> -  return ++count;
> +  return ++val;
>  }
> 
>  static short
> -add_one_short (short count)
> +plus_one_fn_short (short val)
>  {
> -  return ++count;
> +  return ++val;
>  }
> 
>  static int
> -call_callback (int count, gdb::function_view <int(int)> callback)
> +call_callback_int (int val, gdb::function_view <int (int)> callback)
>  {
> -  return callback (count);
> +  return callback (val);
>  }
> 
>  static void
> -call_callback_void (int count, gdb::function_view <void(int)> 
> callback)
> +call_callback_void (int val, gdb::function_view <void (int)> callback)
>  {
> -  callback (count);
> +  callback (val);
>  }
> 
> -struct plus_one_function_object
> +struct plus_one_int_func_obj
>  {
> -  int operator () (int count)
> +  int operator () (int val)
>    {
> -    ++calls;
> -    return ++count;
> +    ++call_count;
> +    return ++val;
>    }
> 
> -  int calls = 0;
> +  /* Number of times called.  */
> +  int call_count = 0;
>  };
> 
>  static void
>  run_tests ()
>  {
>    /* A simple lambda.  */
> -  auto plus_one = [] (int count) { return ++count; };
> +  auto plus_one_lambda = [] (int val) { return ++val; };
> 
>    /* A function_view that references the lambda.  */
> -  gdb::function_view<int (int)> plus_one_view (plus_one);
> +  gdb::function_view<int (int)> plus_one_func_view (plus_one_lambda);
> 
>    /* Check calling the lambda directly.  */
> -  SELF_CHECK (plus_one (0) == 1);
> -  SELF_CHECK (plus_one (1) == 2);
> +  SELF_CHECK (plus_one_lambda (0) == 1);
> +  SELF_CHECK (plus_one_lambda (1) == 2);
> 
>    /* Check calling lambda via the view.  */
> -  SELF_CHECK (plus_one_view (2) == 3);
> -  SELF_CHECK (plus_one_view (3) == 4);
> +  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 (1, [] (int count)
> +  SELF_CHECK (call_callback_int (1, [] (int val)
>      {
> -      return count + 2;
> +      return val + 2;
>      }) == 3);
> 
>    /* Same, passing a named/lvalue lambda.  */
> -  SELF_CHECK (call_callback (1, plus_one) == 2);
> +  SELF_CHECK (call_callback_int (1, plus_one_lambda) == 2);
>    /* Same, passing named/lvalue function_view (should copy).  */
> -  SELF_CHECK (call_callback (1, plus_one_view) == 2);
> +  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_function_object func_obj;
> +  plus_one_int_func_obj func_obj;
>    SELF_CHECK (func_obj (0) == 1);
> -  SELF_CHECK (call_callback (1, func_obj) == 2);
> +  SELF_CHECK (call_callback_int (1, func_obj) == 2);
>    /* Check that the callable was referenced, not copied.  */
> -  SELF_CHECK (func_obj.calls == 2);
> +  SELF_CHECK (func_obj.call_count == 2);
> 
> -  /* Check constructing a function_view over free-function callable,
> +  /* Check constructing a function_view over a free-function callable,
>       and calling it.  */
> -  SELF_CHECK (call_callback (1, add_one) == 2);
> +  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 (1, [] (short count) -> short
> +  SELF_CHECK (call_callback_int (1, [] (short val) -> short
>      {
> -      return count + 2;
> +      return val + 2;
>      }) == 3);
>    /* Same, but passing a function pointer.  */
> -  SELF_CHECK (call_callback (1, add_one_short) == 2);
> +  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 count) -> int
> +  call_callback_void (1, [] (int val) -> int
>      {
> -      return count + 2;
> +      return val + 2;
>      });
>    call_callback_void (1, func_obj);
> -  call_callback_void (1, add_one);
> +  call_callback_void (1, plus_one_fn_int);
> 
>    /* Check that the main ctor doesn't hijack the copy ctor.  */
> -  auto plus_one_view2 (plus_one_view);
> -  auto plus_one_view3 (plus_one_view2);
> -  static_assert (std::is_same<decltype (plus_one_view),
> -		 decltype (plus_one_view2)>::value, "");
> -  static_assert (std::is_same<decltype (plus_one_view),
> -		 decltype (plus_one_view3)>::value, "");
> +  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_view3 (1) == 2);
> +  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_1 = nullptr;
> -  constexpr auto null_func_2 (null_func_1);
> +  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_2 == nullptr, "");
> -  static_assert (nullptr == null_func_2, "");
> -  static_assert (null_func_2 == NULL, "");
> -  static_assert (NULL == null_func_2, "");
> +  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_2 != nullptr), "");
> -  static_assert (!(nullptr != null_func_2), "");
> -  static_assert (!(null_func_2 != NULL), "");
> -  static_assert (!(NULL != null_func_2), "");
> +  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_2, "");
> +  static_assert (!null_func_view_2, "");
> 
>    /* Check the nullptr_t ctor.  */
> -  constexpr gdb::function_view<int(int)> check_ctor_nullptr (nullptr);
> +  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 (add_one);
> +  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);

That's fine with me.


  parent reply	other threads:[~2017-02-22 18:02 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-22 14:50 [PATCH 0/3] Introduce gdb::function_view & fix completion bug Pedro Alves
2017-02-22 14:50 ` [PATCH 1/3] Introduce gdb::function_view Pedro Alves
2017-02-22 15:15   ` Simon Marchi
2017-02-22 17:40     ` Pedro Alves
2017-02-22 17:49       ` [PATCH v1.1 " Pedro Alves
2017-02-22 22:12         ` Yao Qi
2017-02-23 14:49           ` Pedro Alves
2017-02-23 15:11             ` Yao Qi
2017-02-23 15:20               ` Pedro Alves
2017-02-23 15:34                 ` Yao Qi
2017-02-22 22:23         ` Yao Qi
2017-02-23 14:50           ` [PATCH v1.2 " Pedro Alves
2017-02-23 14:58             ` Yao Qi
2017-02-23 14:59               ` Pedro Alves
2017-02-22 18:02       ` Simon Marchi [this message]
2017-02-22 14:50 ` [PATCH 3/3] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index Pedro Alves
2017-02-23 16:02   ` Yao Qi
2017-02-23 17:12     ` Pedro Alves
2017-02-23 17:24     ` [PATCH v2 0/2] " Pedro Alves
2017-02-23 17:24       ` [PATCH v2 2/2] " Pedro Alves
2017-02-24 15:34         ` Yao Qi
2017-02-24 17:15           ` Pedro Alves
2017-02-23 17:24       ` [PATCH v2 1/2] symtab.c: Small refactor Pedro Alves
2017-02-23 21:45         ` Yao Qi
2017-02-24 17:45           ` Pedro Alves
2017-02-22 14:50 ` [PATCH 2/3] Use gdb::function_view in iterate_over_symtabs & co Pedro Alves
2017-02-22 22:40   ` Yao Qi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=61b1e228e36f3fbb0d8bd07432622e45@polymtl.ca \
    --to=simon.marchi@polymtl.ca \
    --cc=gdb-patches@sourceware.org \
    --cc=palves@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox