I’d like to propose three basic functionalities wrt parameter packs.1) Allow expansion of only a single element via pattern...[I].2) Allow in-situ creation of unexpanded non-type parameter packs via Type... N.3) Allow aliases via using Is = std::size_t... 5.
On 06.03.2016, at 19:55, Arthur O'Dwyer <arthur....@gmail.com> wrote:
> I do think addition (1) is immune from this particular problem, but I believe addition (1) is 100% equivalent to syntactic sugar for std::get<I>(std::forward_as_tuple(pattern...)).
Not quite, as it doesn’t work for type-packs, only for value-packs. For value-packs, since C++14 both std::get as well as std::forward_as_tuple are constexpr so that is also covered. It’s still a lot to write compared to pattern...[I] and it feels very indirect. It also internally will most likely have to go through the type-pack case.
print(Ns*2+1)...[I];
> What if C++1z were to add a single "unwrapped" version of std::get, something like this?
>
> template<size_t Idx, typename... T>
> inline __attribute__((__visibility__("hidden"), always_inline))
> decltype(auto) getp(T&&... t) {
> return std::get<Idx>(std::forward_as_tuple(t...));
> }
>
> That is, getp would be to std::get as std::experimental::invoke is to std::experimental::apply.
> The library vendor would be free to optimize the internal implementation of getp so that it could be constant-time instead of logarithmic (or worse) in the value of Idx.
> Would this solve your problem (1) without a core language change? That is, if you could take your existing hypothetical code using params...[I] and rewrite it to use std::getp<I>(params...), would that be acceptable?
> (I can confirm that just because a call to std::getp<I> appears in the source code for a TU does not mean that the compiler is forced to generate out-of-line code for std::getp<I>, because empirically Clang does not generate it. The above gives perfect codegen at -O3. I can definitely believe that it doesn't give perfect performance in terms of compile time, but I think that would be just a QoI issue, solvable by the vendor in whatever way they found most appropriate.)
Make “getp” constexpr, but yes, that’s another version of the above select, where your usage is somehow easier: “std::getp<I>(vs...)”. This could work, together with “std::type_by_index_t<I, Ts...>”. Still both interfaces are quite different and we certainly need better names :)
FWIW, there was http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3761.html which used std::type_at and std::value_at.
using Ts = typename... X<int>::pack; //like `using T = typename X<int>::type;`
using Vs = std::size_t... Y<int>::values_pack;
template<typename T, typename X>
using Xs = T... X::pack; // using "Name" = "Category" "Value";
template<int I>
using seq_p = std::size_t... std::make_index_sequence<I>::pack;
using Vs = seq_p<I>; //like `using T = aligned_storage_t<16>;`
return f(g(Xs<T, Ts...>{}...));
//or
return f(g(Xs<T, Ts...>{})...);
template<typename T>
using id_t = T;
template<typename... T>
using id_p = typename... T; //Identity pack `id_p<T...> === T`
template<typename... T>
using apply_p = typename... id_t<T>; //Transform `apply_p<T...> === id_t<T>`
template<typename T, typename... Ts>
using Xpp = typename... ... apply_p<T, Ts>; //Transform `Xpp<T, Ts...> === apply_p<T, Ts>` this is pack of pack
return f(g(Xpp<Tsingle, Ts...>{}...)...); //if Ts=[T1, T2] then this is equal `return f(g(id_t<Tsingle>{}, id_t<Tsingle>{}), g(id_t<T1>{}, id_t<T2>{}));`
> On 13.03.2016, at 20:32, inkwizyt...@gmail.com wrote:
>> On Sunday, March 13, 2016 at 3:59:58 PM UTC+1, Daniel Frey wrote:
>> > On 13.03.2016, at 15:54, Daniel Frey <d.f...@gmx.de> wrote:
>> >
>> >> template<typename T, typename X>
>> >> using Xs = T... X::pack; // using "Name" = "Category" "Value";
>> >
>> > This would be a major problem, exactly what I tried to avoid. The usage could otherwise be an expression like
>> >
>> > template<typename... Ts> void bar() { return f(g(Xs<Ts>{}...)...; } // return ???
>>
>> That should be
>>
>> template<typename T, typename... Ts>
>> void bar()
>> {
>> return f(g(Xs<T, Ts>{}...)...);
>> }
>>
>> but you get the idea... :)
>
> `Xs` is pack and can be expand only once. First `...` apply to `Xs` not to `Ts`. This mean `Xs<T, Ts>` is invalid because `Ts` is not expand.
Ts as also a pack. This is not defined by todays C++, hence you are expressing an idea you have, not a current reality. Other options exist of what the above could mean in the future. No option is the right one, it is a choice.
> I think it should work like:
>
> return f(g(Xs<T, Ts...>{}...));
This would be unambiguous. Let's say Xs = {A,B,C}, Ts = {int,long}:
return f(g(Xs<T, Ts...>{}...));
return f(g(Xs<T, int, long>{}...));
return f(g(A<T, int, long>{}, B<T, int, long>{}, C<T, int, long>{}));
> return f(g(Xs<T, Ts...>{})...);
return f(g(Xs<T, Ts...>{}...));
return f(g(Xs<T, int, long>{}...));
return f(g(A<T, int, long>{}, g(B<T, int, long>{}), g(C<T, int, long>{})));
OK, how do you expand to:
return f(g(A<T, int>{}, A<T, long>{}), g(B<T, int>{}, B<T, long>{}), g(C<T, int>{}, C<T, long>{})); // (e1)
or
return f(g(A<T, int>{}, B<T, int>{}, C<T, int>{}), g(A<T, long>{}, B<T, long>{}, C<T, long>{})); // (e2)
or
return f(g(A<T, int>{}, A<T, long>{}, B<T, int>{}, B<T, long>{}, C<T, int>{}, C<T, long>{})); // (e3)
or
return f(g(A<T, int>{}, B<T, int>{}, C<T, int>{}, A<T, long>{}, B<T, long>{}, C<T, long>{})); // (e4)
> As we see this is only one expansion. To have second one, I think we would need pack of pack to handle it:
And this is one of the things I'd like to avoid: Creating a pack of packs (and by induction a pack of packs of packs, etc.). It means creating new entities that don't exist today in a compiler's backend. The complexity to implement a proposal which does something like that is much higher and the risk of introducing new bugs makes it less likely that a proposal will be accepted.
Anyways, if you *really* want to do something like this I think it makes much more sense to have "partial expansion" something like:
return f(g(Xs<T, Ts>{}...[Ts])...); // generates (e1)
^^^^^^^
possible syntax when the brackets don't contain an integral constant as in my proposal (1), but instead a pack's name (or a set of names) that occur in the expression. Each step requires all packs mentioned to have the same length, but separate steps may have different lengths. Only those packs are expanded in each step which are listed, the result is another pack of expressions which contain unexpanded packs which are then expanded by the second ellipsis. The above therefore yields (e1). Complete list:
return f(g(Xs<T, Ts>{}...[Ts])...); // generates (e1)
return f(g(Xs<T, Ts>{}...[Xs])...); // generates (e2)
return f(g(Xs<T, Ts>{}...[Ts]...)); // generates (e3)
return f(g(Xs<T, Ts>{}...[Xs]...)); // generates (e4)
But this is probably material for a second proposal and I haven't really thought it through - it's just an idea. Unless everyone thinks it's a brilliant idea and should be part of my proposal right now :-P
-- Daniel
It is an interesting idea, but I don't feel it is fundamental for handling packs. Instead it as applying the ellipsis to an expression which contain *no* packs. I think we are currently wrapping too many things into std::tuple when most of the time we'd like to handle packs directly. Unpacking a tuple is therefore, in my mind at least, trying to solve to wrong problem. It might still be worth as an additional feature to look into (maybe via an "operator..."), but it'll create a lot of headaches when an expression which is followed by an ellipsis contains both packs and object with an "operator...".
template<typename Tuple>
using Expand = typename... Tuple::pack; //of if tuple don't have we can crate helper template that will extract parameter pack from tuple
return f(forward<Expand<Tuple>>(tuple)...); //`Expand<Tuple>` is pack that can be expanded by `...`
No, that would be pattern[I]...
Putting the index first means that indexing into a parameter pack would have backwards syntax from indexing into anything else in the language. That's objectionable.
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/ajLcDl8GbpA/unsubscribe.
To unsubscribe from this group and all its topics, 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/nc7082%24p34%241%40ger.gmane.org.
template<typename... T>
struct X
{
using... Ts = T;
};
template<typename... Z>
auto Y()
{
return F(X<Z>::Ts{}...); //exactly same situation like `Tp<Z>` where `Tp` is templated pack.
};
template<typename... T>
void f()
{
g(T::pack...); //each of `T` have member pack definition `typename... pack = int, long;`
}