Variadic decomposition

202 views
Skip to first unread message

Richard Thorell

unread,
Oct 12, 2016, 6:51:24 AM10/12/16
to ISO C++ Standard - Future Proposals
Hey,

After looking at the structured bindings paper [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf] I thought, why don't we allow the members to be automatically unpacked into it's components and forwarded to a method?
We have this functionality when it comes to tuples today, using std::get<> and std::index_sequence. But what if it was a built-in language feature and a one-liner? And worked with any structure!

Proposal:
template <class... Args>
void foo(void(*func)(Args...), std::tuple<Args...> args)
{
    func
(auto{ ... } = args);
}
 
The syntax is questionable, but I wanted it to follow the structured bindings syntax as close as possible. The auto keyword can have const, l-value and r-value reference modifiers which will be appended to each argument. Also, the decomposition can only be used within a function/constructor call.

Maybe this has already been discussed somewhere?

Any feedback is appreciated!

Thanks,
Richard

Alisdair Meredith

unread,
Oct 12, 2016, 6:58:31 AM10/12/16
to std-pr...@isocpp.org
A library function with these semantics is already present in the C++17 CD, called 'apply'.  What benefit do you expect by complicating the language with a compiler feature to delivery (mostly) the same effect?

(By using the INVOKE treatment, the library version is actually slightly more capable than a likely language feature, dispatching through pointer-to-members etc. as well.)

AlisdairM

Sent from my iPad
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/0d627ddf-dec9-4051-abaf-e6e6f8dbeaaa%40isocpp.org.

Richard Thorell

unread,
Oct 12, 2016, 7:09:02 AM10/12/16
to ISO C++ Standard - Future Proposals
Hey,

Yeah I'm aware of std::apply but it still only operates on std::tuple. If you have your own structure and you want to decompose it, there's no generic way to do so.

template <class T>
constexpr auto struct_to_fw_tuple(T& any_struct)
{
   
return std::forward_as_tuple(auto&{...} = any_struct);
}

template <class F, class Tuple>
constexpr decltype(auto) apply(F&& func, Tuple&& tuple)
{
   
return std::invoke(std::forward<F>(func), auto&&{...} = tuple);
}

The snippet in both post were just examples of usages.

Cheers,
Richard

D. B.

unread,
Oct 12, 2016, 7:13:02 AM10/12/16
to std-pr...@isocpp.org
On Wed, Oct 12, 2016 at 12:09 PM, Richard Thorell <richard...@king.com> wrote:
Hey,

Yeah I'm aware of std::apply but it still only operates on std::tuple. If you have your own structure and you want to decompose it, there's no generic way to do so.



The tuple need not be std::tuple, and instead may be anything that supports std::get and std::tuple_size; in particular, std::array and std::pair may be used. 

Simon Brand

unread,
Oct 12, 2016, 7:13:16 AM10/12/16
to std-pr...@isocpp.org

Hi Richard,

std::apply doesn't just operate on std::tuple; it uses std::get and std::tuple_size, which is the same method by which you can add structured binding support to your own class (unless the proposal has been updated since I last looked). 

Thanks,
Simon
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6867504e-d4e5-4e2b-859c-3040f7cdc05f%40isocpp.org.

-- 
Simon Brand
Staff Software Engineer
Codeplay Software Ltd
Level C, Argyle House, 3 Lady Lawson St, Edinburgh, EH3 9DR
Tel: 0131 466 0503
Fax: 0131 557 6600
Website: http://www.codeplay.com
Twitter: https://twitter.com/codeplaysoft

This email and any attachments may contain confidential and /or privileged information and is for use by the addressee only. If you are not the intended recipient, please notify Codeplay Software Ltd immediately and delete the message from your computer. You may not copy or forward it, or use or disclose its contents to any other person. Any views or other information in this message which do not relate to our business are not authorized by Codeplay software Ltd, nor does this message form part of any contract unless so stated.
As internet communications are capable of data corruption Codeplay Software Ltd does not accept any responsibility for any changes made to this message after it was sent. Please note that Codeplay Software Ltd does not accept any liability or responsibility for viruses and it is your responsibility to scan any attachments.
Company registered in England and Wales, number: 04567874
Registered office: 81 Linkfield Street, Redhill RH1 6BY 

Richard Thorell

unread,
Oct 12, 2016, 7:23:14 AM10/12/16
to ISO C++ Standard - Future Proposals
Hi,

Yes, I take that back. Should've been types that supports std::get/std::tuple_size (to little coffee still). But it still requires you to add an overload for your structure right?

With a variadic decomposition you could use any struct without any overloads and you could benefit from all good stuff that std::tuple gives you (# of members, compare operators etc.).

Thanks,
Richard

ps. just got another coffee

Simon Brand

unread,
Oct 12, 2016, 7:31:01 AM10/12/16
to std-pr...@isocpp.org

I believe that with variadic decomposition you would only be able to use it for classes which only have public member data (or have those data members in a single direct base class). For any class without that property, you'd still need to write implementations of get and tuple_size. So the main benefit of your suggestion would be removing the need to implement a couple of functions for a small set of classes, but to get that it's mandating a language change and some new syntax.

I do like the idea of the proposal, but I think that the costs outweigh the benefits over the std::apply solution.

-- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group. To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org. To post to this group, send email to std-pr...@isocpp.org. To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c28ca0ca-b59d-4e06-8c79-52f7cd859a49%40isocpp.org.

Barry Revzin

unread,
Oct 12, 2016, 9:31:32 AM10/12/16
to ISO C++ Standard - Future Proposals

Proposal:
template <class... Args>
void foo(void(*func)(Args...), std::tuple<Args...> args)
{
    func
(auto{ ... } = args);
}
 
The syntax is questionable, but I wanted it to follow the structured bindings syntax as close as possible. The auto keyword can have const, l-value and r-value reference modifiers which will be appended to each argument. Also, the decomposition can only be used within a function/constructor call.


I think the closer thing to structured bindings would be to extend the latter to allow for introducing variadic names. That would allow: 

template <class F, class... Args>
void foo(F func, std::tuple<Args...> tup)
{
    auto& [args...] = tup;
    func
(args...);
}

That's not all in one line, but I think it's actually not terrible. 

Richard Thorell

unread,
Oct 12, 2016, 9:36:47 AM10/12/16
to ISO C++ Standard - Future Proposals
That was my first thought actually, like an extension to structured bindings. Maybe this syntax is even better, since it allows the pack to be re-used.

Thanks,
Richard

Nicol Bolas

unread,
Oct 12, 2016, 9:56:14 AM10/12/16
to ISO C++ Standard - Future Proposals


There have already been discussions about having a way to transform tuples and tuple-like types into parameter packs of their elements. One suggested syntax would look like this:

template <class F, class... Args>
void foo(std::tuple<Args...> args)
{
    func
([:]args...);
}

This syntax allows you to do things like `func(std::forward<Args>([:]args)...)`. It would even allow you to call arbitrary expressions on tuple elements, or allow them to participate in fold expressions. To get the sum of all elements of a tuple, you do `(0 + ... + [:]args)`.

That's much more flexible than your "structured binding"-based stuff here.

Tony V E

unread,
Oct 12, 2016, 11:29:47 AM10/12/16
to Simon Brand
Should we just make get and tuple_size automatically work on all PODs?


Sent from my BlackBerry portable Babbage Device
From: Simon Brand
Sent: Wednesday, October 12, 2016 7:31 AM
Subject: Re: [std-proposals] Variadic decomposition

Ville Voutilainen

unread,
Oct 12, 2016, 11:37:14 AM10/12/16
to ISO C++ Standard - Future Proposals
On 12 October 2016 at 18:29, Tony V E <tvan...@gmail.com> wrote:
Should we just make get and tuple_size automatically work on all PODs?



Yes. Proposals welcome, I can co-author.

Peter Koch Larsen

unread,
Oct 12, 2016, 1:28:25 PM10/12/16
to std-pr...@isocpp.org
That sounds like a very nice idea.

-- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group. To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org. To post to this group, send email to std-pr...@isocpp.org. To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c28ca0ca-b59d-4e06-8c79-52f7cd859a49%40isocpp.org.
-- 
Simon Brand
Staff Software Engineer
Codeplay Software Ltd
Level C, Argyle House, 3 Lady Lawson St, Edinburgh, EH3 9DR
Tel: 0131 466 0503
Fax: 0131 557 6600
Website: http://www.codeplay.com
Twitter: https://twitter.com/codeplaysoft

This email and any attachments may contain confidential and /or privileged information and is for use by the addressee only. If you are not the intended recipient, please notify Codeplay Software Ltd immediately and delete the message from your computer. You may not copy or forward it, or use or disclose its contents to any other person. Any views or other information in this message which do not relate to our business are not authorized by Codeplay software Ltd, nor does this message form part of any contract unless so stated.
As internet communications are capable of data corruption Codeplay Software Ltd does not accept any responsibility for any changes made to this message after it was sent. Please note that Codeplay Software Ltd does not accept any liability or responsibility for viruses and it is your responsibility to scan any attachments.
Company registered in England and Wales, number: 04567874
Registered office: 81 Linkfield Street, Redhill RH1 6BY 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Simon Brand

unread,
Oct 12, 2016, 4:32:21 PM10/12/16
to std-pr...@isocpp.org

That sounds like a good possibility. I'd also be interested in co-authoring a proposal about it, although it's not something I've done for the C++ standard before.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Arthur O'Dwyer

unread,
Oct 13, 2016, 4:07:13 AM10/13/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 8:29:47 AM UTC-7, Tony V E wrote:
Should we just make get and tuple_size automatically work on all PODs?

Nitpick: You don't mean "PODs", you mean "structs with no non-public non-static data members".

Not a nitpick: std::apply and decomposition declarations are inconsistent in how they do things.

    namespace X { struct S {}; /* ... */ };

    X::S x;
    std::apply(f, x);  // Line 1
    auto [a,b,c] = x;  // Line 2

Line 1 above will result in instantiation of std::tuple_size<S>::value, std::get<0>(x), and so on; it explicitly will not attempt to look in namespace X, because it is not defined in terms of ADL.
Line 2 above will result in instantiation of std::tuple_size<S>::value, X::get<0>(x), and so on; it explicitly will not attempt to instantiate std::get<0>(x), because all it does is plain old ADL lookup. Namespace std is privileged for "tuple_size" but NOT privileged for "get".

Making std::get magically DTRT for struct types will help with Line 1, but it will not allow you to simplify the wording that controls Line 2; decomposition declarations will still need a special magic exemption for struct types.

IMO the current state of affairs is a mess, but I have no silver bullet to get us out of trouble.

–Arthur

Richard Thorell

unread,
Oct 13, 2016, 9:19:07 AM10/13/16
to ISO C++ Standard - Future Proposals
This seems to cover what has been discussed here:
https://groups.google.com/a/isocpp.org/forum/?#!topic/std-proposals/Umm9lHwUMxo

I do like the idea of extending std::get and std::tuple_size for any standard layout type.

Cheers,
Richard

Vicente J. Botet Escriba

unread,
Oct 13, 2016, 2:08:54 PM10/13/16
to std-pr...@isocpp.org
Hi,

I have taken a little different approach in

    Product-Type access  - https://github.com/viboes/std-make/blob/master/doc/proposal/reflection/p0327r1.md

I have added a nested product_type scope for size/get/element.

I understand that been able to define by default tuple_size<T>/tuple_element<I,T>/get<I> for "structs with no non-public non-static data members" would be super. However,
I don't know how we can define them by default for "structs with no non-public non-static data members" and h tuple_size/ tuple_element/ave get as customization points for de-structure binding at the same time. Maybe I'm missing something evident.

Any comment welcome.

Vicente

P.S. Unfortunately I will not have time to change the wording respect to p0327r0.



Vicente J. Botet Escriba

unread,
Oct 13, 2016, 3:02:25 PM10/13/16
to std-pr...@isocpp.org
See " parameter packs outside of templates" - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0341r0.html



Vicente

Matthew Woehlke

unread,
Oct 14, 2016, 10:52:57 AM10/14/16
to std-pr...@isocpp.org
On 2016-10-12 06:51, Richard Thorell wrote:
> After looking at the structured bindings paper I thought, why don't
> we allow the members to be automatically unpacked into it's
> components and forwarded to a method?
>
> Proposal:
> template <class... Args>
> void foo(void(*func)(Args...), std::tuple<Args...> args)
> {
> func(auto{ ... } = args);
> }
>
> Maybe this has already been discussed somewhere?

First, to the folks pointing at std::apply / std::invoke... those can't
call constructors. They are not sufficient.

As for prior art... I've (not yet officially) proposed:

foo([:]tuple_like...);
auto f = Foo([:]tuple_like...);
foo([2:4]tuple_like...);
foo([1:]tuple_like...);
foo([:-2]tuple_like...);
// ...etc.

Note that the above syntax is proposed that `tuple_like` can be either a
concrete, tuple-like type *or* a parameter pack. That is, given a
parameter pack `pp`, the syntax `[:]pp` is a legal no-op.

Note: The output of a non-singular pack indexing operation is a
parameter pack (i.e. `[:]tuple_like` yields a parameter pack).

(A singular pack indexing operation is e.g. `[0]pp`. Note that
`[0]tuple_like` is equivalent to `get<0>(tuple_like)`.)

--
Matthew

D. B.

unread,
Oct 14, 2016, 10:57:42 AM10/14/16
to std-pr...@isocpp.org
On Fri, Oct 14, 2016 at 3:52 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:

First, to the folks pointing at std::apply / std::invoke... those can't
call constructors. They are not sufficient.



That's what std::make_from_tuple is for.

Matthew Woehlke

unread,
Oct 14, 2016, 11:09:51 AM10/14/16
to std-pr...@isocpp.org
On 2016-10-14 10:57, D. B. wrote:
> On Fri, Oct 14, 2016 at 3:52 PM, Matthew Woehlke wrote:
>> First, to the folks pointing at std::apply / std::invoke... those can't
>> call constructors. They are not sufficient.
>
> That's what std::make_from_tuple is for.

Please show the library equivalent of this:

auto x = Foo{[:]t1..., [-1]t2, [0]t2, [2:-1]t2..., v};

A language feature is easy to use and works in every context without
having to remember several library functions.

You can *probably* do the above using only only library features (though
start throwing in references, and things likely get more interesting),
but it's going to be *hugely* more typing and will involve temporaries
(that the compiler may or may not be able to optimize away).

--
Matthew

Richard Thorell

unread,
Oct 14, 2016, 11:16:26 AM10/14/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
Hi Matthew,

I do like your suggestion a lot. Solves the problem I wanted and with better functionality.

Cheers,
Richard

Matthew Woehlke

unread,
Oct 14, 2016, 11:24:46 AM10/14/16
to std-pr...@isocpp.org
On 2016-10-14 11:09, Matthew Woehlke wrote:
> On 2016-10-14 10:57, D. B. wrote:
>> On Fri, Oct 14, 2016 at 3:52 PM, Matthew Woehlke wrote:
>>> First, to the folks pointing at std::apply / std::invoke... those can't
>>> call constructors. They are not sufficient.
>>
>> That's what std::make_from_tuple is for.
>
> Please show the library equivalent of this:
>
> auto x = Foo{[:]t1..., [-1]t2, [0]t2, [2:-1]t2..., v};

...not to mention:

// let 'vec' be equivalent to an array of T
auto dot = T{1} * [:]vec...;
auto manhattan_distance = T{0} + std::abs([:]vec)...;

Being able to turn a tuple-like¹ into a parameter pack is MUCH more
useful than having to keep creating one-off library functions to do the
same thing.

(¹ ...or product type, or aggregate, or whatever becomes the "blessed" term)

--
Matthew

Arthur O'Dwyer

unread,
Oct 14, 2016, 2:36:06 PM10/14/16
to ISO C++ Standard - Future Proposals
On Fri, Oct 14, 2016 at 8:24 AM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2016-10-14 11:09, Matthew Woehlke wrote:
> On 2016-10-14 10:57, D. B. wrote:
>> On Fri, Oct 14, 2016 at 3:52 PM, Matthew Woehlke wrote:
>>> First, to the folks pointing at std::apply / std::invoke... those can't
>>> call constructors. They are not sufficient.
>>
>> That's what std::make_from_tuple is for.
>
> Please show the library equivalent of this:
>
>   auto x = Foo{[:]t1..., [-1]t2, [0]t2, [2:-1]t2..., v};

...not to mention:

  // let 'vec' be equivalent to an array of T
  auto dot = T{1} * [:]vec...;
  auto manhattan_distance = T{0} + std::abs([:]vec)...;

Being able to turn a tuple-like¹ into a parameter pack is MUCH more
useful than having to keep creating one-off library functions to do the
same thing.

Agreed, but there are fundamental semantic problems that you'd need to overcome before anything like this could get into the language.  So while it looks really nifty, I would strongly discourage anyone from saying "Oh, we should JUST standardize something like this and then we don't need (some other achievable library feature) anymore."  There's no such thing as JUST standardizing Matthew's syntax as currently proposed.

See my previous posts in this group
and the following example code.

int main() {
    std::tuple<int> t1 { 1, 2, 3 };
    std::tuple<int,int> t2 { 4, 5, 6 };
    std::tuple<int,int,int> t3 { 7, 8, 9 };
    auto tt = std::make_tuple(t1, t2, t3);  // a tuple of tuples

    f([:]tt ...);   // f(t1, t2, t3);
    f(g([:]tt) ...);  // f(g(t1), g(t2), g(t3));
    f(g([:]tt ...));  // f(g(t1, t2, t3));
    f(g([:][:]tt ...) ...);  // f(g(1,2,3), g(4,5,6), g(7,8,9));  ???
    f(g([:][:]tt ... ...));  // f(g(1, 2, 3, 4, 5, 6, 7, 8, 9));  ???
    ???;  // f(g(1,4,7), g(2,5,8), g(3,6,9)); ???
    ???;  // f(g(1, 4, 7, 2, 5, 8, 3, 6, 9));  ???
}

–Arthur

Nicol Bolas

unread,
Oct 14, 2016, 3:48:55 PM10/14/16
to ISO C++ Standard - Future Proposals
On Friday, October 14, 2016 at 2:36:06 PM UTC-4, Arthur O'Dwyer wrote:
On Fri, Oct 14, 2016 at 8:24 AM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2016-10-14 11:09, Matthew Woehlke wrote:
> On 2016-10-14 10:57, D. B. wrote:
>> On Fri, Oct 14, 2016 at 3:52 PM, Matthew Woehlke wrote:
>>> First, to the folks pointing at std::apply / std::invoke... those can't
>>> call constructors. They are not sufficient.
>>
>> That's what std::make_from_tuple is for.
>
> Please show the library equivalent of this:
>
>   auto x = Foo{[:]t1..., [-1]t2, [0]t2, [2:-1]t2..., v};

...not to mention:

  // let 'vec' be equivalent to an array of T
  auto dot = T{1} * [:]vec...;
  auto manhattan_distance = T{0} + std::abs([:]vec)...;

Being able to turn a tuple-like¹ into a parameter pack is MUCH more
useful than having to keep creating one-off library functions to do the
same thing.

Agreed, but there are fundamental semantic problems that you'd need to overcome before anything like this could get into the language.  So while it looks really nifty, I would strongly discourage anyone from saying "Oh, we should JUST standardize something like this and then we don't need (some other achievable library feature) anymore."

The point of the feature is not to say we don't need standard library tools. The point of the feature is that we don't need nearly so many of them, and even then, the implementations of them become a lot more reasonable.
 
 There's no such thing as JUST standardizing Matthew's syntax as currently proposed.

See my previous posts in this group
and the following example code.

int main() {
    std::tuple<int> t1 { 1, 2, 3 };
    std::tuple<int,int> t2 { 4, 5, 6 };
    std::tuple<int,int,int> t3 { 7, 8, 9 };
    auto tt = std::make_tuple(t1, t2, t3);  // a tuple of tuples

    f([:]tt ...);   // f(t1, t2, t3);
    f(g([:]tt) ...);  // f(g(t1), g(t2), g(t3));
    f(g([:]tt ...));  // f(g(t1, t2, t3));
    f(g([:][:]tt ...) ...);  // f(g(1,2,3), g(4,5,6), g(7,8,9));  ???
    f(g([:][:]tt ... ...));  // f(g(1, 2, 3, 4, 5, 6, 7, 8, 9));  ???
    ???;  // f(g(1,4,7), g(2,5,8), g(3,6,9)); ???
    ???;  // f(g(1, 4, 7, 2, 5, 8, 3, 6, 9));  ???
}

I don't think this is too difficult of a problem to work out.

The tuple-to-pack operator should attach before the ... operator. Therefore, `[:][:]tt...` means "convert tuple to pack, then convert tuple to pack, then unpack". However, since a pack is not a tuple, the second application should be il-formed.

Your issue is essentially, how do I get the system to produce the given effects? Well, the answer is this: `[:]` can't do everything for you. What it does do is make the resulting code decidedly less verbose than it could have been:

f(g(1,2,3), g(4,5,6), g(7,8,9));

You have to use helper function:

f(std::apply(g, [:]tt)...);

Not as neat as being pure. But try to image doing it without the `[:]`, and you'll see why it is so much better.

It also allows things like `std::apply(g, [:]tt) + ... + 0` and similar trickery.


f(g(1, 2, 3, 4, 5, 6, 7, 8, 9));

You're talking about flattening a tuple of tuples. Obviously, parameter pack syntax doesn't provide for such a thing.

Like this above, this requires a utility function that can concatenate tuple-like types into a tuple. With that, it's pretty easy:

f(g([:]std::flatten_tuples([:]tt...)...));

A little verbose, but flattening tuples-of-tuples isn't something you need to do every day.

f(g(1,4,7), g(2,5,8), g(3,6,9));

This is doable with a utility function that acts on tuples, much like a zip iterator/range. It turns a sequence of tuple-like types of the same size into an array of tuples of the corresponding elements:

f(std::apply(g, [:]std::zip_tuples([:]tt...))...);

Nobody ever said that `[:]` syntax was the beginning and the end of tuple handling and unpacking. We will certainly need utility functions to cover specialized cases. And the above code can be quite complex to read. But this is mainly due to the complexity of the cases themselves.

Matthew Woehlke

unread,
Oct 14, 2016, 5:26:54 PM10/14/16
to std-pr...@isocpp.org
On 2016-10-14 15:48, Nicol Bolas wrote:
> On Friday, October 14, 2016 at 2:36:06 PM UTC-4, Arthur O'Dwyer wrote:
>> On Fri, Oct 14, 2016 at 8:24 AM, Matthew Woehlke wrote:
>>> Being able to turn a tuple-like¹ into a parameter pack is MUCH more
>>> useful than having to keep creating one-off library functions to do the
>>> same thing.
>>
>> Agreed, but there are fundamental semantic problems that you'd need to
>> overcome before anything like this could get into the language. So while
>> it looks really nifty, I would strongly discourage anyone from saying "Oh,
>> we should JUST standardize something like this and then we don't need (some
>> other achievable library feature) anymore."
>
> The point of the feature is not to say we don't need standard library
> tools. The point of the feature is that we don't need nearly so many of
> them, and even then, the implementations of them become a lot more
> reasonable.

Right.

>> There's no such thing as JUST standardizing Matthew's syntax as currently
>> proposed. [Consider] the following example code.
>>
>> std::tuple<int> t1 { 1, 2, 3 };
>> std::tuple<int,int> t2 { 4, 5, 6 };
>> std::tuple<int,int,int> t3 { 7, 8, 9 };
>> auto tt = std::make_tuple(t1, t2, t3); // a tuple of tuples
>>
>> f([:]tt ...); // f(t1, t2, t3);
>> f(g([:]tt) ...); // f(g(t1), g(t2), g(t3));
>> f(g([:]tt ...)); // f(g(t1, t2, t3));
>> f(g([:][:]tt ...) ...); // f(g(1,2,3), g(4,5,6), g(7,8,9)); ???
>> f(g([:][:]tt ... ...)); // f(g(1, 2, 3, 4, 5, 6, 7, 8, 9)); ???
>> ???; // f(g(1,4,7), g(2,5,8), g(3,6,9)); ???
>> ???; // f(g(1, 4, 7, 2, 5, 8, 3, 6, 9)); ???
>
> I don't think this is too difficult of a problem to work out.
>
> The tuple-to-pack operator should attach before the ... operator.
> Therefore, `[:][:]tt...` means "convert tuple to pack, then convert tuple
> to pack, then unpack".

Exactly. In the paper I am working on, I explicitly give prefix `[]`
higher precedence than postfix `...`, so both the 'what do these mean'
examples above are ill-formed. However...

> However, since a pack is *not a tuple*, the second application
> should be ill-formed.

I *also* specify slicing as working on *either* parameter packs or
product types. So the first application converts the tuple to a pack,
the second does nothing, and then you have `pack......`, which is
ill-formed because the second `...` is no longer being applied to a pack.

> *f(g(1,2,3), g(4,5,6), g(7,8,9));*
>
> You have to use helper function:
>
> f(std::apply(g, [:]tt)...);
>
> Not as neat as being pure. But try to image doing it without the `[:]`, and
> you'll see why it is so much better.

Yup, that's about what I came up with.

> *f(g(1, 2, 3, 4, 5, 6, 7, 8, 9));*
>
> You're talking about flattening a tuple of tuples. Obviously, parameter
> pack syntax doesn't provide for such a thing.

Agreed again. In order to make this work with *just* the language
feature, it would be necessary to apply a fold expression on prefix
`[]`. I don't see an obvious and sensible way to specify that.

> Like this above, this requires a utility function that can concatenate
> tuple-like types into a tuple. With that, it's pretty easy:
>
> f(g([:]std::flatten_tuples([:]tt...)...));

That requires a temporary. It can also be done (without the temporary)
by modifying std::apply to accept a list of tuples rather than just one:

std::apply_helper<int n>(auto func, auto... args)
{
// n is number of already-unpacked arguments
constexpr auto r = sizeof...(args) - n; // remaining tuples
if constexpr (r == 0)
return func(args...);

auto&& t = [n]args;
auto k = sizeof...(t);
return apply_helper<n + k>(func, [:n]args, [:]t..., [n+1:]args);
}

std::apply(auto func, auto... tuples)
{
return apply_helper<0>(func, tuples);
}

(I'd be interested to see that implemented *without* my feature... *and*
without creating any temporary tuples.)

> Nobody ever said that `[:]` syntax was the beginning and the end of tuple
> handling and unpacking. We will certainly need utility functions to cover
> specialized cases. And the above code can be quite complex to read. But
> this is mainly due to the complexity of the cases themselves.

Right. I'm not attempting to solve interleaving or reversing; I'd like
to see pack generators for those tasks (but they possibly require pack
indexing, so...).

These don't strike me as problems that need to be solved before the
feature can move forward.

--
Matthew
Reply all
Reply to author
Forward
0 new messages