Grouping placeholders for bind

213 views
Skip to first unread message

toma...@gmail.com

unread,
Jul 23, 2013, 4:40:57 PM7/23/13
to std-pr...@isocpp.org
In the discussion of mem_fn with additional parameter Jonathan Wakely proposed the idea of new placeholder type named all,
 that will forward all of the arguments to the functor and suggested that it could be extended to more generic group placeholders.
Example:
auto f = std::bind(&A::foo, a, std::placeholders::all);
Will create functor that will bind an object/pointer a with the function member pointer and forwards all supplied arguments to the functor,
so:
f(t1, t2, ..., tN)
Will be equivalent to:
a.foo
(t1, t2, ..., tN)
//I assumed a is an object in the example, but it will support anything that INVOKE does.

I would like to share my thoughts on the approach how such placeholders can be defined in the extend-able manner (so the user can define
its own group placeholders).

1. At this moment the standard defines the is_placeholder traits that provides both functionality: informs that the type placeholder and provides
the number of forwarder argument. I would like to split that behavior, to 2 classes:
  a) is_placeholder<T> would be used to determine if the type is placeholder
  b) placeholder_positions<T,N> will provide zero (please see motivation bellow) or more index of argument to be forwarder (index should be 1based to
      uniform with standard bind). The second argument N would mean actual number of argument provided to invocation of the bind (so we cane genereate
      index of all arguments). The placeholder position should be derived from std::integer_sequence<std::size_t, ...> class in case when is_placeholder<T>::value
    is true.
  c) to main the backward compatibility the, default implementation of
placeholder_positions would be:
    template<typename T, std::size_t N>
    struct placeholder_positions :
      
std::integer_sequence<std::size_t, std::is_placeholder<T>::value>
    {}

      so for the single argument placeholder, is_placeholder can be used in the same way as today, but bind depend only for 
placeholder_positions
  to get argument  to forward.

2. There should be 2 group placeholders defined in the standard:
    a) _all, that will forward all arguments, definition using proposed specializations would be:
       template<>
   struct is_placeholder<_all> : std::true_type {};

  
template<std::size_t N>
   struct
placeholder_positions<_all, N>
     : shift<s
td::make_integer_sequence<std::size_t, N>, 1> {}
  b)  _from<N> that will forward arguments all arguments form Nth. The _from<N> should be ill formed if number of arguments is less than N. For exactly N-1
        arguments there should be empty sequence generated - if there is need to be sure that at least N arguments, then _N, _from<N+1> may be used.
       Implementation:
       template<std::size_t N>
   struct is_placeholder<_from<N>> : std::true_type {};

  
   t
emplate<std::size_t N>
   struct
placeholder_positions<_all, ArgCount>
     : enable_if<
ArgCount + 1 <= N,     
                 shift<s
td::make_integer_sequence<std::size_t, ArgCount - N  + 1>, N> {}
  c) The user can define its own placeholders group via specializations.

3. I would define new placeholder in the namespace std::placeholders, but that may lead to the some annoying problems to the user if there where named simply
    all and from. Most of the code that uses bind, looks like:
    using namespace
std::placeholders;
    std::bind(f, _1, a, _2);

    While there is really low possibility to have variables named _1, _2, ... in the user code, there is pretty much a chance that the user code would define viarbles
    named all or from and cause the crash of bind.  That it the reason I choosed _all and _from instead, which look pretty uniform with _1. Another solution would be
    to define it in the different namespace.

What are you thoughts about this idea.

Xeo

unread,
Jul 23, 2013, 5:22:39 PM7/23/13
to std-pr...@isocpp.org
I have played around with implementing such an `easy_bind` (as I call it) before, which can be found here and is really easy (especially in C++14 when you can just use `decltype(auto)` for the return type).

You will notice that I used `std::function<R(FArgs...)>` instead of a generic `class F` in the public interface - the reason is that not all function objects can have the number of their parameters determined. The `operator()` may be either overloaded, templated, or both, rendering any attempt to get a specific signature moot.

Of course, the overhead (if actually there) of `std::function` is likely unwanted in such a general purpose function. However, there are really only two callable entities where the signature can be inspected: function-pointers and member-function-pointers. As such, with the interface of `std::bind` today, you can't really do better than that.


The second argument N would mean actual number of argument provided to invocation of the bind (so we cane genereate index of all arguments)
Here is the problem. First, the number of (bound) arguments passed to `std::bind` is irrelevant for the placeholders that are needed; these need, if anything, to be determined from the passed callable object. Second, notice that Jonathan specifically did not mention any indices. This `std::placeholders::all` form requires a very different implementation that simply forwards all arguments that are passed at the invocation of the returned bind object. It cannot be done with the current interface and numbered placeholders, simply because some functions don't even *have* an upper bound on the number of arguments - variadic templates say hello.

toma...@gmail.com

unread,
Jul 24, 2013, 1:47:30 AM7/24/13
to std-pr...@isocpp.org


W dniu wtorek, 23 lipca 2013 23:22:39 UTC+2 użytkownik Xeo napisał:
I have played around with implementing such an `easy_bind` (as I call it) before, which can be found here and is really easy (especially in C++14 when you can just use `decltype(auto)` for the return type).

You will notice that I used `std::function<R(FArgs...)>` instead of a generic `class F` in the public interface - the reason is that not all function objects can have the number of their parameters determined. The `operator()` may be either overloaded, templated, or both, rendering any attempt to get a specific signature moot.

Of course, the overhead (if actually there) of `std::function` is likely unwanted in such a general purpose function. However, there are really only two callable entities where the signature can be inspected: function-pointers and member-function-pointers. As such, with the interface of `std::bind` today, you can't really do better than that.

The second argument N would mean actual number of argument provided to invocation of the bind (so we cane genereate index of all arguments)
Here is the problem. First, the number of (bound) arguments passed to `std::bind` is irrelevant for the placeholders that are needed; these need, if anything, to be determined from the passed callable object. Second, notice that Jonathan specifically did not mention any indices. This `std::placeholders::all` form requires a very different implementation that simply forwards all arguments that are passed at the invocation of the returned bind object. It cannot be done with the current interface and numbered placeholders, simply because some functions don't even *have* an upper bound on the number of arguments - variadic templates say hello.
The sentence should look like (I ommited it part while writing):
  The second argument N would mean actual number of argument provided to the operator() of functor created as a result of bind.
The expansion of the group placeholder is only done with the context when bind result is invoked so the number of arguments that was passed to it would be enough to generate indicies that will forward alll of the and placeholders::all can be implemented.

toma...@gmail.com

unread,
Jul 27, 2013, 6:40:27 AM7/27/13
to std-pr...@isocpp.org

The second argument N would mean actual number of argument provided to invocation of the bind (so we cane genereate index of all arguments)
Here is the problem. First, the number of (bound) arguments passed to `std::bind` is irrelevant for the placeholders that are needed; these need, if anything, to be determined from the passed callable object. Second, notice that Jonathan specifically did not mention any indices. This `std::placeholders::all` form requires a very different implementation that simply forwards all arguments that are passed at the invocation of the returned bind object. It cannot be done with the current interface and numbered placeholders, simply because some functions don't even *have* an upper bound on the number of arguments - variadic templates say hello.

It can be done and to prove it I have prepared the almost standard conforming implementation of bind function in C++11 (tested on clang 3.2 and gcc 4.8.1) that supports customizable group placeholders. The thinks that are missing are:
  1. bind<R> - because its add nothing and implementation would be trivial
  2. volatile, const volatile overloads of bind invoke operator - this is cause by lack of std::get overload for tuples which such qualification (const is supported).
  3. _from<N> is not variable template (not supported)
The implementation can be found on: https://github.com/tomaszkam/proposals/tree/master/bind.

Firstly the test cases can be found in test.cpp. That test includes varidatic template cases and also covers combination of group and non group placeholders.

Firstly the new placeholders traits are placed in the file placeholder_traits.hpp. I decided to include new is_placeholder  that forwards to std::is_placeholder traits to be sure that new group placeholders won't work with std::bind. You may also see default implementation of placeholder_positions that provides backward compatibility with single argument placeholders definied using only std::is_placeholder. To mark bind expression the std::is_bind_expression is used.

The implementation of bind expression is placed in file bind.hpp. This implementation has no hardcoded placeholders types and depends only on placeholder traits.

The definition of actual grouping placeholders can be found at placeholders.hpp. The _from<N> is at this point alias template but should be changed to variable template in future.


Are there any interest in defining group placeholders for the bind? If so I think the ability to define custom group placeholders by the user is must have, to avoid exponential grow of bind implementation. In the proposed approach implementation of _group<2, 3, 7> placeholder (which may be found pretty usefull) would look like:

template<std::size_t.... Is>
struct group_placeholder {};

template<std::size_t.... Is>
struct is_placeholder<group_placeholder<Is...>> : true_type {};

template<std::size_t.... Is, std::size_t ArgCount>
struct placeholder_positions<group_placeholder<Is...>, ArgCount> : integral_sequence<std::size_t, Is...>
{
   //static assert to avoid check if  all Is are less ArgCount would be recomended but no actually needed.
};

namespace placeholders
{
   template<std::size_t.... Is>
   group_placeholder<Is....> _group;
};


Reply all
Reply to author
Forward
0 new messages