template <typename... Transitions> auto Switch(TrieNode<Transitions...> ...) {
// ...
switch (ch) {
case 0: ... special code ... break;
case (Transitions::Char) { // Note: no ":" // the label has to be constexpr (nothing new)
return checkTrie(Transitions::Next(),str,...); }...
}
// return error;
}
template <int... Is> void function(...)
{
// ...some normal code
{
f(Is);
// more block code, probably using Is - or other packs of same length
}...
// ...some more normal code
}
// helper to execute('map') parameter pack: pass({(f(args),0)...});
inline void pass(const std::initializer_list<int> &) {}
template <typename... Transitions> auto Switch(TrieNode<Transitions...> ...)
// ...
switch (ch) {
{ case Transitions::Char:
return checkTrie(Transitions::Next(),...); }...
}
// ...
{
// statements as in a normal block
}...
On Tuesday, July 5, 2016 at 3:43:26 PM UTC+2, inkwizyt...@gmail.com wrote:On Tuesday, July 5, 2016 at 3:28:08 PM UTC+2, smili...@googlemail.com wrote:Hello together,
during development of my "Compile-time trie"-based string matcher, I wanted to use the compiler's advanced code generation for switch statements (instead of non-optimized recursion/unrolling), but had to use preprocessor based techniques (cttrie_sw256-boost.tcc, cttrie_sw32-boost.tcc), due to the lack of an appropriate pack-expansion.
And because the set of generated case-values is mostly sparse, using a table-based technique (i.e. generate an array of function pointers via pack expansion) was also not adequate.A short web search also brought up some StackOverflow questions asking for switch/case with parameter packs.
[...]
This requires almost no changes to the C++ syntax, because a switch-block is not very different from other blocks (even permitting constructs like Duff's device).
Tobias
Similar proposition float around there couple of times.
One example:
https://groups.google.com/a/isocpp.org/forum/?fromgroups#!searchin/std-proposals/packs/std-proposals/LoDJDCDDyH8/L3TUatFwm98J
More general cases:
https://groups.google.com/a/isocpp.org/forum/?fromgroups#!searchin/std-proposals/packs/std-proposals/GHdwZbYY8L0/Jqf_yfvyHAAJ
https://groups.google.com/a/isocpp.org/forum/?fromgroups#!searchin/std-proposals/packs/std-proposals/zym3j2iJyZg/su9ERhh28GwJ
https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-proposals/2hdpLnXJKkQ
I'm well aware of existing expression-expansion ideas - but these (probably) won't help with the switch statement.Even more, if we have block/statement-expansion, you can easily use it in an expression context by wrapping it in an immediately executed lambda.But what I don't quite understand: Given the general interest in such a feature (for which you gave some more examples), why do all these proposals seem to go nowhere?
Adding Block/Statement expansion seem quite easy - both the implementation and the specification. At least, I haven't heard of any substantial issues in those areas.I'm not talking about anything fancy, just
{
// statements as in a normal block
}...with the explicit allowance for use in conjunction with switch/case (and I've given some the design considerations in my initial mail).Why not bake such simple basics into the language and consider all the other ideas only as a second step?At least, I'd be helpful to have SOME guidance from the standardization body, whether they prefer such a simple solution or whether they'd like more work done on "for constexpr"(e.g.).
And, if block/statement expansion won't move forward (for now), maybe add a more specific expansion for switch/case (something akin to the "first syntax idea" from my first mail)?Tobias
Adding Block/Statement expansion seem quite easy - both the implementation and the specification. At least, I haven't heard of any substantial issues in those areas.
At least, I'd be helpful to have SOME guidance from the standardization body, whether they prefer such a simple solution or whether they'd like more work done on "for constexpr"(e.g.).
And, if block/statement expansion won't move forward (for now), maybe add a more specific expansion for switch/case (something akin to the "first syntax idea" from my first mail)?
Things don't get standardized because someone posts a suggestion in a mailing list. Things get standardized because someone puts work and effort into it, pushing it forward at actual standards meetings.
This ML is useful for figuring out what a proposal ought to be. But you can't treat it as a place where you drop off an idea and hope that someone else does something with it. Well, you can, but if you do, you shouldn't be surprised if your idea never goes anywhere.
Adding Block/Statement expansion seem quite easy - both the implementation and the specification. At least, I haven't heard of any substantial issues in those areas.
Then it shouldn't be difficult for you to prove that. After all, if it's that easy to implement, you ought to be able to fork Clang or GCC and implement it, right? If it's that easy to specify, then you should be able to write the appropriate standards wording for it.
It's easy to say that someone else's job is easy. It's not so easy when you have to do it yourself.
statement:
// ...
compound-statement
compound-statement '...' // <-- new
// ....
template <int... Is>
void bar()
{
{
printf("%d\n",Is);
}...
switch (1) {
{
case Is: printf("%d\n",Is);
}...
}
}
// ... bar<0,1,2>();
template <int I> struct int_c {};
template <int I> struct MyType {
typedef int_c<I> type;
};
template <int... Is> void foo()
{
{
struct X {
static void bar(typename MyType<Is>::type mt) {
// error: pack expansion does not contain any unexpanded parameter packs
}
};
// ...
}...
}
template <int... Is> void foo()
{ { // alternative: "([]{"
struct X {
static void bar() {
printf("%d\n",Is);
}
}
}... // alternative: "return 0; }() || ...);" // does not work either
}
On Wednesday, July 6, 2016 at 3:25:22 AM UTC+2, Nicol Bolas wrote:Things don't get standardized because someone posts a suggestion in a mailing list. Things get standardized because someone puts work and effort into it, pushing it forward at actual standards meetings.Sure. But not all of us are in a position to participate in standards meetings.
At least, I'd be helpful to have SOME guidance from the standardization body, whether they prefer such a simple solution or whether they'd like more work done on "for constexpr"(e.g.).And, if block/statement expansion won't move forward (for now), maybe add a more specific expansion for switch/case (something akin to the "first syntax idea" from my first mail)?
some info on for constexpr discussed here: https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-proposals/2hdpLnXJKkQ Right now I'm playing with gcc code (using my free time so progress is not rapid enough). I want to have implementation I can play with and share with other people on this list and then start to work on proposal paper.
template <typename T> struct type_c { typedef T type; };
template <typename... Ts> // ...
([&](auto t){
using T=typename decltype(t)::type;
//... instead of "continue;" -> "return true;"
//... instead of "break;" -> "return false;"
return true; // must be inserted at the end
}(type_c<Ts>()) && ... );
// we have two packs, T1 and T2
template<typename...> pack{};
for constexpr (typename A : pack<T1..., T2...>) { } //pack merge
for constexpr (typename A : typename zip<pack<T1...>, pack<T2...>>::type) { } //zip two parameters pack, `type` is `pack<std::pair<T1, T2>...>`
for constexpr (typename A : pack<T1...>) { for constexpr (typename B : pack<T2...>) { } } //cross of two packs, this could be same pack too
for constexpr (typename A : pack<int, long, char>) { } //usage without any parameter pack and outside any template
while(y)
{
switch(x)
{
default: while(x)
{
--x;
case 1: --x;
case 5: --x; if (x == 7) break; //this is inner `while` break.
}
break; //break for quitting after inner `while`
case 10: x += 10; continue; //this is outer `while` continue.
for constexpr (int A : Cases...)
{
case 10 + A: func<A>(x);
if (A & 1) continue; //`for constexpr` continue
break; //`for constexpr` break
}
break; //break for quitting after `for`
}
}
On Thursday, July 7, 2016 at 3:08:34 PM UTC+2, smili...@googlemail.com wrote:Yes, `for constexpr` is far more verbose but this give us lot more control over that happens. Dots work great when you want expand one parameter pack but if is more of them or you want start do some complex thing this will start be problematic.Image you want use Cartesian product of two parameters pack. Without introducing completely new syntax you can't handle it.
Of corse `for` have limitation too, but is lot of easier remove it.
Only thing that `for constexpr` need to handle nearly all possible use cases is allow it to extract template parameters from concrete templates (it would need have parameters like `template<typename... T>` or `template<auto... A>`):
// we have two packs, T1 and T2
template<typename...> pack{};
for constexpr (typename A : pack<T1..., T2...>) { } //pack merge
for constexpr (typename A : typename zip<pack<T1...>, pack<T2...>>::type) { } //zip two parameters pack, `type` is `pack<std::pair<T1, T2>...>`
for constexpr (typename A : pack<T1...>) { for constexpr (typename B : pack<T2...>) { } } //cross of two packs, this could be same pack too
for constexpr (typename A : pack<int, long, char>) { } //usage without any parameter pack and outside any template
And for `break` and `continue`. Only real problem there is `continue` because `break` can be easy fix by adding one after `for`. Another is this interaction should be well know for every one because is already same for normal loops: [...]
using... Us = pack<int,char,long>;
// ...
{ doSomething<Us> }...
// ...
On Thursday, July 7, 2016 at 4:29:05 PM UTC+2, inkwizyt...@gmail.com wrote:On Thursday, July 7, 2016 at 3:08:34 PM UTC+2, smili...@googlemail.com wrote:Yes, `for constexpr` is far more verbose but this give us lot more control over that happens. Dots work great when you want expand one parameter pack but if is more of them or you want start do some complex thing this will start be problematic.Image you want use Cartesian product of two parameters pack. Without introducing completely new syntax you can't handle it.You can implement product<pack<T1...>,pack<T2...>>::type as a library solution just like zip<..>. The only difference is that, for Us=pair<T1,T2>... you might want to add "using A = Us::first...;" and "using B = Us::second...;" for your convenience.Of corse `for` have limitation too, but is lot of easier remove it.
Only thing that `for constexpr` need to handle nearly all possible use cases is allow it to extract template parameters from concrete templates (it would need have parameters like `template<typename... T>` or `template<auto... A>`):
// we have two packs, T1 and T2
template<typename...> pack{};
for constexpr (typename A : pack<T1..., T2...>) { } //pack merge
for constexpr (typename A : typename zip<pack<T1...>, pack<T2...>>::type) { } //zip two parameters pack, `type` is `pack<std::pair<T1, T2>...>`
for constexpr (typename A : pack<T1...>) { for constexpr (typename B : pack<T2...>) { } } //cross of two packs, this could be same pack too
for constexpr (typename A : pack<int, long, char>) { } //usage without any parameter pack and outside any template
And for `break` and `continue`. Only real problem there is `continue` because `break` can be easy fix by adding one after `for`. Another is this interaction should be well know for every one because is already same for normal loops: [...]The for constexpr proposal meshes two things together that don't have to: One half is the semantic of "for", esp. wrt. to the creation of a break/continue-scope. The other half is the "inline"-introduction of a new parameter pack (i.e. without another indirection like function call / template class).
The first half is IMO only weakly motivated; when you think of it, even the name "for constexpr" is not exactly fitting, because you're (most importantly) dealing with types, not constexpr values.
The second half can be dealt with without touching break/continue - or even forcing an expansion. Think about:
using... Us = pack<int,char,long>;
// ...
{ doSomething<Us> }...
// ...That's just a simple example, but the same thing works the same with product, zip, and all the stuff from above (you can add "using A = Ts::first;" as you like).
{ someType<Us>::thisIsPackToo; }...
The real questions are: How will "using..." (or "for constexpr") know how to "iterate the pack", i.e. find the variadic part?
When we look at how range-for was specified, we can see that it does not "bless" a certain "range"-type, but uses whatever iterator begin() / end() would return. So we're now talking about "meta"-iterators over types, like Boost MPL - or Fusion - or Hana - or ... has. It's not like the one true solution in this space has already been around long enough just waiting to get standardized.
template<typename...>
struct pack
{
};
template<typename T>
struct Extract
{
};
template<template<typename... > typename A, typename... T>
struct Extract<A<T...>>
{
using type = pack<T...>;
};
template<typename A, typename B>
struct Z
{
};
using Y = typename Extract<Z<int, double>>::type; //pack<int, double>
The real questions are: How will "using..." (or "for constexpr") know how to "iterate the pack", i.e. find the variadic part?
When we look at how range-for was specified, we can see that it does not "bless" a certain "range"-type, but uses whatever iterator begin() / end() would return. So we're now talking about "meta"-iterators over types, like Boost MPL - or Fusion - or Hana - or ... has. It's not like the one true solution in this space has already been around long enough just waiting to get standardized.
template<typename... T>
void foo_block_expanded(T... t) {
{
if constexpr
(index_of_v<T, T...> < index_of_v<std::nullptr_t, T...>
) { // break on std::nullptr_t in the type list
bar(t);
if constexpr (should_not_skipp_v<T>) // contunue if type should be skipped
do_the_thing(t);
}
}...
}
template<typename... T>
struct types {
using list = T;
};
void foo1() {
for constexpr (typename T: types<int, std::string
>::list) {
T val;
std::cin >> val;
std::cout << val << std::endl;
}
}
void foo2() {
using typelist = types<int, std::string>::list;
{
typelist val;
std::cint >> val;
std::cout << val << std::endl;
}...
}
template<typename... L1>
struct A {
template<typename... L2>
void foo(L2... a) {
{
{
bar<L1>(a);
}...
}...
{
baz<L2>(a);
}...
}
};
The real questions are: How will "using..." (or "for constexpr") know how to "iterate the pack", i.e. find the variadic part?
When we look at how range-for was specified, we can see that it does not "bless" a certain "range"-type, but uses whatever iterator begin() / end() would return. So we're now talking about "meta"-iterators over types, like Boost MPL - or Fusion - or Hana - or ... has. It's not like the one true solution in this space has already been around long enough just waiting to get standardized.
The reason why runtime range for is specified in terms of begin/end function and iterators is nececsity to work with user provided collection types. In case of compile time for constexpr we have only one builtin collection type: variadic pack and it's easy to specify how it should be iterated. And I'm strongly against of introducing user types whith compile time collection semantic. It adds too much complexity to the C++ metaprogramming system which is overcomplicated already. We don't need to lazy iteration over bytes received from the socket transformet into parsed messages accessible as iterable range at compile time.
for constexpr (int I=0; I<sizeof...(Ts); ++I) {
foo<Ts...[I]>();
}
I feel that the discussion here can be much more usefull if we start to compare both appreaches "for constexpr" and "block expansion" for applicability in desired usecases. [...]
- Task to unpack more then one parameters pack. Trivial with for constexpr but how the following code should be compiled:
template<typename... L1>
struct A {
template<typename... L2>
void foo(L2... a) {
{
{
bar<L1>(a);
}...
}...
{
baz<L2>(a);
}...
}
};I assume that baz<L2>(a) expansion should be turned into baz<L2[0]>(a[0]); ... baz<L2[N]>(a[N]); as it work in C++11 pack expansion rules. Looks like the inner part of the bar-expansion should work in a diferent way. I haven't found good way to solve this issue yet.
// either:
{
using X=L1; // (error: declaration contains unexpanded parameter pack)
{
bar<X>(a);
}...
}...
// or:
{
auto x=a; // (error: initializer contains unexpanded parameter pack 'a')
{
bar<L1>(x);
}...
}...