template<typename ...Args)
func(Args&& ...pack)
{
auto rev_agg = ...; //Some reverse-aggregate.
auto test1 = make_tuple(func(rev_agg)...); //Compile error; nothing in the expression is a value sequence.
auto test2 = make_tuple(func([*]rev_agg)...); //Success. Treats `rev_agg` as though it were a parameter pack.
auto test3 = make_tuple(func(rev_agg, pack)...); //Success. `rev_agg` gets no special treatment. Every call to `func` gets the same `rev_agg`, just as it would in current code.
auto test4 = make_tuple(func([*]rev_agg, pack)...); //Success. Unpacks both `rev_agg` and `pack`. Only works if the two are the same size, just like for two parameter packs.
}
auto ...stuff = {func([*]rev_agg)...};
auto {x, y, z} = {func([*]rev_agg)...};
auto (w, u, v} = {pack...};
<code>
<auto... t> = make_tuple(foo()...);
</code>
I decided to put the proposal on ice until the structured bindings proposal has matured.
tuple<int, float> f = ...
tuple<bool, std::string> g = ...
auto ...thing1 = func([*]f, [*]g)...;
auto ...thing2 = func([*]f, g)...;
auto ...thing3 = func(f, [*]g)...;
auto ...thing4 = func(f, g)...;
auto... name = <value sequence expression>
sizeof...(<value sequence>)
<value sequence expression>...
auto {x, y, z} = args; //Function parameter pack decomposed into 3 elements.
auto {x, y, z} = unpackable; //Unpackable type stored via normal structured binding.
auto {x, y, z} = [*]unpackable; //Value sequence stored via structured binding. Identical to previous statement.
auto {x, y, z} = {func([*]rev_agg)...};
auto {x, y, z} = func([*]rev_agg);
On 2016-01-19 10:40, Nicol Bolas wrote:
> auto ...thing1 = func([*]f, [*]g)...;
I alluded to this in my other reply, but I think it bears repeating...
what does this (RHS) mean?
- { func(f<0>, g<0>), func(f<1>, g<1>) }
- { func(f<0>, f<1>, g<0>), func(f<0>, f<1>, g<1>) }
- { func(f<0>, g<0>, g<1>), func(f<1>, g<0>, g<1>) }
- func(f<0>, f<1>, g<0>, g<1>)
I could legitimately want *any* of those.
Conflating concepts (template
argument packs and value sequence unpacking of tuple-likes) makes it
impossible to specify which. Not to mention that I'm not convinced the
syntax isn't overly obtuse.
If, instead, we have apply_each (and zip), I can be precise:
- apply_each([](auto p){return func([*]p);}, [*]zip(f, g))
- apply_each([](auto v){return func([*]f, v);}, [*]g)
- apply_each([](auto v){return func(v, [*]g);}, [*]f)
- func([*]f, [*]g)
Now there is no ambiguity as to my intent.
On 2016-01-19 11:44, Nicol Bolas wrote:
> On Tuesday, January 19, 2016 at 11:31:31 AM UTC-5, Matthew Woehlke wrote:
>> On 2016-01-19 10:40, Nicol Bolas wrote:
>>> auto ...thing1 = func([*]f, [*]g)...;
>>
>> I alluded to this in my other reply, but I think it bears repeating...
>> what does this (RHS) mean?
>>
>> - { func(f<0>, g<0>), func(f<1>, g<1>) }
>> - { func(f<0>, f<1>, g<0>), func(f<0>, f<1>, g<1>) }
>> - { func(f<0>, g<0>, g<1>), func(f<1>, g<0>, g<1>) }
>> - func(f<0>, f<1>, g<0>, g<1>)
>>
>> I could legitimately want *any* of those.
>
> And my syntax can provide *all* of them:
>
> func([*]f, [*]g)...;
> func([*]f..., [*]g)...;
> func([*]f, [*]g...)...;
> func([*]f..., [*]g...);
Why do I have to unpack it *twice*? Ugh...
> [...] lots and lots of heinous syntax.
I find your proposal much too complicated and severely lacking in
rationale why we need such as a *language* feature. (I have yet to see
you offer a rationale, besides "hey, wouldn't it be great if...".)
auto test4 = make_tuple(func([*]rev_agg, pack)...);
auto zipped = std::zip(rev_agg, make_tuple(pack...));
auto test4 =
std::apply_each([](auto pair){ return func([*]pair); }, zipped);
auto test4 = make_tuple(func([*]rev_agg, forward<Args>(pack))...);
On 2016-01-19 14:15, Nicol Bolas wrote:
> Your mechanism is too simplified to be of *general* use.
I disagree. I can think of immediate examples of unpacking in place (and
some have been given). I would like to see a reasonable example for:
auto result = make_tuple(func([*]f..., [*]g)...);
...along with a rationale why this merits a language feature.
p.s. If we have genuine promotion of value sequences to parameter packs,
why can't we do this?
auto&&... g_unpacked = g;
auto result = make_tuple(func([*]f, g_unpacked)...);
> ... The *only part* of your proposal that absolutely needs to be a
> *language* feature is the ability to unpack a tuple into a
> braced-init-list.
I think you're severely understating the usefulness of this case. I
don't feel that being able to unpack into e.g. constructor calls is so
rare as to be so lightly glossed over.
> Oh, and the temporary in this case must be named. Weren't you one of those
> who were saying that naming things is hard
Naming *API* is hard :-). I dub the temporary 't'. Or 't0'. Or whatever;
the names of temporar^Wlocal scratch variables don't matter the way the
names of API types do.
On 2016-01-20 01:50, Arthur O'Dwyer wrote:
> Aren't you both (Nicol and Matthew) proposing language features? Matthew is
> proposing `[*]`, and Nicol is proposing `[*]...`. I don't see how either
> of those can be implemented as a library-only feature.
Yes, to all points. I feel like I've provided better examples, however,
and that the specification of my version is significantly simpler. Maybe
if Nicol will present a specification of his version, I will change my
mind, but it still seems to me that his is more complicated and harder
to use.
> Notice that in the previous parenthetical I had to write std::tuple_size_v<decltype(std::tie([*]x))>
> - 1 to express the size of the "comma-separated sequence" [*]x under
> Matthew's proposal. I think that's really the simplest thing that would
> compile!
Uh... why? What's wrong with std::tuple_size_v(x)? Recall that in the
discussions of expanding the notion of "tuple-like", it's generally been
implied that tuple_size would be defined, in addition to get<N>.
> On Tuesday, January 19, 2016 at 12:23:25 PM UTC-8, Matthew Woehlke wrote:
>> p.s. If we have genuine promotion of value sequences to parameter packs,
>> why can't we do this?
>>
>> auto&&... g_unpacked = g;
>> auto result = make_tuple(func([*]f, g_unpacked)...);
>
> Because you can't have variables of parameter-pack type. Parameter packs
> aren't first-class citizens in C++.
I think you missed the point; that is exactly what I meant by "If we
have genuine promotion of value sequences to parameter packs". Note the
"auto..." syntax.
> - should tuples be convertible back into packs, or into some other kind of
> entity?
I'm not proposing "some other kind of entity". I'm proposing an
immediate code (AST?) transform at point of use. Introducing some new
(or even existing) "other kind of entity" is specifically something I
want to *avoid*.
> The questions of "why can't I declare a pack on the stack", or "why can't I
> init-capture a pack", or "why can't I declare a class A<T...> that has
> members of types T..." are all good questions, but are completely
> orthogonal to the two questions above. And they'll still be there for the
> solving later.
But that's somewhat the point... the question is, if we want the ability
as in the above example *anyway*, then what value is left in Nicol's
proposal over mine (besides that it saves a little typing)?
This would
make the common case (just want to unpack in place) easy and the
uncommon case possible, vs. making both possible, but also both ugly.
Anecdotal, of course, but I can't think that I have *ever* wanted to
invoke a function for each member of a tuple-like.
That suggests that they're rare
On 2016-01-20 01:50, Arthur O'Dwyer wrote:
> (Matthew doesn't really mean that literally anyway, unless he's proposing
> to allow
>
> [*]x;
The non-complicated version of the proposal would indeed allow this; it
would expand to `get<0>(x), get<1>x, ..., get<N>(x);`, which is valid
code. Silly, but valid.
> Notice that in the previous parenthetical I had to write std::tuple_size_v<decltype(std::tie([*]x))>
> - 1 to express the size of the "comma-separated sequence" [*]x under
> Matthew's proposal. I think that's really the simplest thing that would
> compile!
Uh... why? What's wrong with std::tuple_size_v(x)? Recall that in the
discussions of expanding the notion of "tuple-like", it's generally been
implied that tuple_size would be defined, in addition to get<N>.
> On Tuesday, January 19, 2016 at 12:23:25 PM UTC-8, Matthew Woehlke wrote:
>> p.s. If we have genuine promotion of value sequences to parameter packs,
>> why can't we do this?
>>
>> auto&&... g_unpacked = g;
>> auto result = make_tuple(func([*]f, g_unpacked)...);
>
> Because you can't have variables of parameter-pack type. Parameter packs
> aren't first-class citizens in C++.
I think you missed the point; that is exactly what I meant by "If we
have genuine promotion of value sequences to parameter packs". Note the
"auto..." syntax.
> The questions of "why can't I declare a pack on the stack", or "why can't I
> init-capture a pack", or "why can't I declare a class A<T...> that has
> members of types T..." are all good questions, but are completely
> orthogonal to the two questions above. And they'll still be there for the
> solving later.
But that's somewhat the point... the question is, if we want the ability
as in the above example *anyway*, then what value is left in Nicol's
proposal over mine (besides that it saves a little typing)? This would
make the common case (just want to unpack in place) easy and the
uncommon case possible, vs. making both possible, but also both ugly.
Okay, having said that, I *can* think of one case... can you show how
you would simplify this code?
stream << "begin" << get<0>(x) << get<1>(x)
<< ... << get<N>(x) << "end";
(Rules: 'stream' must be named exactly once, and no fair creating a
helper function. Strong preference to avoid assigning 'stream' to a
scratch variable.)
For bonus points, include `", "` between each item of `x` (okay to
include after the last item also).