digitalmars.D - Typed variadic template syntax?
- bearophile (32/32) Jan 28 2014 This is not an enhancement request, but it presents a potential
- Stanislav Blinov (12/18) Jan 28 2014 Well, if you're going for a direct translation, that would be
- Andrei Alexandrescu (13/31) Jan 28 2014 int value(int xs[]...) {
- Jakob Ovrum (3/4) Jan 28 2014 Ew! :)
- Timon Gehr (11/23) Jan 29 2014 import std.range, std.algorithm;
- Stanislav Blinov (3/7) Jan 29 2014 Sadly, you can't build a struct (with N int fields) or an enum
- Andrei Alexandrescu (3/9) Jan 29 2014 mixin
- Stanislav Blinov (3/7) Jan 29 2014 m|
- Etienne (40/47) Jan 29 2014 That was a problem for me today XD . Couldn't make a auto
- Etienne (2/7) Jan 29 2014 I'd like to take the opportunity to say how much I'd love to be
- Peter Alexander (18/26) Jan 29 2014 You can use TypeTuple for static foreach.
- Idan Arye (18/45) Jan 30 2014 Two problems:
-
Ilya Yaroshenko
(6/56)
Jan 30 2014
1) You can use mixin(format("
%( %s %) ", - Timon Gehr (12/24) Jan 30 2014 The following code fails to compile:
- Etienne (16/26) Jan 30 2014 This written as a static foreach or declarative foreach, would be
- Ilya Yaroshenko (4/32) Jan 30 2014 It works now:
- Ilya Yaroshenko (20/46) Jan 30 2014 I am wrong, foreach always has own scope
- Ilya Yaroshenko (3/51) Jan 30 2014 import std.string; =)
- Idan Arye (23/27) Jan 30 2014 That works for simple cases. Complex cases require that you write
- Dicebot (4/4) Jan 30 2014 Andrei has stated at least once that he agrees about usefulness /
- Andrei Alexandrescu (4/7) Jan 30 2014 Yah, we should have an easier means of iteratively injecting stuff into
- deadalnix (4/20) Jan 30 2014 You got to mixin the whole stuff, not field by field, and you
- Idan Arye (22/44) Jan 30 2014 Except it doesn't work inside classes and structs - it complains
- Timon Gehr (3/9) Jan 31 2014 This is the corresponding issue:
- Dicebot (5/7) Jan 30 2014 This is why I think using term "declaration foreach" is less
- inout (4/46) Jan 29 2014 This allocates memory and as such an inferior solution. I'd
- anonymous (2/18) Jan 29 2014 no
This is not an enhancement request, but it presents a potential enhancement. This is C++11 code: template<int... digits> struct value; template<> struct value<> { static const int v = 0; }; template<int first, int... rest> struct value<first, rest...> { static int const v = 10 * value<rest...>::v + first; }; You can translate it to D like this: enum isInt(T) = is(T == int); template value(xs...) if (allSatisfy!(isInt, xs)) { static if (xs.length == 0) enum value = 0; else enum value = xs[0] + 10 * value!(xs[1 .. $]); } This D code: template value(xs...) if (allSatisfy!(isInt, xs)) { Is slower to compile than this D code: template value(xs...) { So we could allow D code like: template value(int[] xs...) { I think it could be faster than using "if (allSatisfy!(isInt, xs))" and it's nicer looking. On the other hand I don't know how much common are template instantiations with values all of the same type (like all ints as in this case) in D code. Opinions welcome. Bye, bearophile
Jan 28 2014
On Wednesday, 29 January 2014 at 00:14:31 UTC, bearophile wrote:On the other hand I don't know how much common are template instantiations with values all of the same type (like all ints as in this case) in D code. Opinions welcome. Bye, bearophileWell, if you're going for a direct translation, that would be more like template value(ints...) { static if (!ints.length) enum value = 0; else static if (!is(ints[0] == int)) static assert(false); else enum value = ints[0] + 10*value!(ints[1..$]); } But that indeed illustrates a certain disconcert: we can already have variadic functions of the same type (void foo(int[] a...)). It would certainly be nice to have the same for template parameters.
Jan 28 2014
On 1/28/14 4:14 PM, bearophile wrote:This is not an enhancement request, but it presents a potential enhancement. This is C++11 code: template<int... digits> struct value; template<> struct value<> { static const int v = 0; }; template<int first, int... rest> struct value<first, rest...> { static int const v = 10 * value<rest...>::v + first; }; You can translate it to D like this: enum isInt(T) = is(T == int); template value(xs...) if (allSatisfy!(isInt, xs)) { static if (xs.length == 0) enum value = 0; else enum value = xs[0] + 10 * value!(xs[1 .. $]); }int value(int xs[]...) { int result = 0; foreach (i; xs) { result += 10 * result + i; } return result; } unittest { static assert(value(1, 2) == 21); } Andrei
Jan 28 2014
On Wednesday, 29 January 2014 at 07:02:00 UTC, Andrei Alexandrescu wrote:int value(int xs[]...) {Ew! :)
Jan 28 2014
On 01/29/2014 08:01 AM, Andrei Alexandrescu wrote:int value(int xs[]...) { int result = 0; foreach (i; xs) { result += 10 * result + i; } return result; } unittest { static assert(value(1, 2) == 21); } ...import std.range, std.algorithm; int value(int xs[]...) { return reduce!((a,b)=>10*a+b)(0,xs.retro); } unittest{ static assert(value()==0); static assert(value(1)==1); static assert(value(1,2)==21); static assert(value(1,2,3)==321); }
Jan 29 2014
On Wednesday, 29 January 2014 at 09:50:13 UTC, Timon Gehr wrote:import std.range, std.algorithm; int value(int xs[]...) { return reduce!((a,b)=>10*a+b)(0,xs.retro); }Sadly, you can't build a struct (with N int fields) or an enum this way.
Jan 29 2014
On 1/29/14 1:53 AM, Stanislav Blinov wrote:On Wednesday, 29 January 2014 at 09:50:13 UTC, Timon Gehr wrote:mixin Andreiimport std.range, std.algorithm; int value(int xs[]...) { return reduce!((a,b)=>10*a+b)(0,xs.retro); }Sadly, you can't build a struct (with N int fields) or an enum this way.
Jan 29 2014
On Wednesday, 29 January 2014 at 16:39:31 UTC, Andrei Alexandrescu wrote:On 1/29/14 1:53 AM, Stanislav Blinov wrote:m|Sadly, you can't build a struct (with N int fields) or an enum this way.mixin
Jan 29 2014
On Wednesday, 29 January 2014 at 09:53:15 UTC, Stanislav Blinov wrote:On Wednesday, 29 January 2014 at 09:50:13 UTC, Timon Gehr wrote:That was a problem for me today XD . Couldn't make a auto fct(string[] choices ...)(string[] id, string[] key ...) but I managed to find a workaround... // KEYWORDS enum USING = "USING.", VAR = "VAR.", LIST = "LIST.", SET = "SET.", ZSET = "ZSET.", HASHMAP = "HASHMAP.", LOCK = "LOCK.", UNLOCK = "UNLOCK.", SKIPCACHE = "SKIPCACHE.", INCREMENT = "INCREMENT.", DECREMENT = "DECREMENT.", FRONT = "FRONT.", BACK = "BACK.", PUSH = "PUSH.", POP = "POP.", REPLACE = "REPLACE.", TRIM = "TRIM.", RETURNS = "RETURNS.", LENGTH = "LENGTH.", EXISTS = "EXISTS.", VALUE = "VALUE.", KEY = "KEY.", STREAM = "STREAM.", ERROR = "ERROR.", void exec(string command)(){ foreach(str ; choice.splitter(".")){ writeln(str); } } void main() { exec!(USING ~ VAR ~ ADD ~ USING ~ LIST ~ INSERT ~ LOCK)(); }import std.range, std.algorithm; int value(int xs[]...) { return reduce!((a,b)=>10*a+b)(0,xs.retro); }Sadly, you can't build a struct (with N int fields) or an enum this way.
Jan 29 2014
void exec(string command)(){ foreach(str ; choice.splitter(".")){ writeln(str); } }I'd like to take the opportunity to say how much I'd love to be able to do a static foreach rather than use recursion.
Jan 29 2014
On Wednesday, 29 January 2014 at 21:18:06 UTC, Etienne wrote:You can use TypeTuple for static foreach. void foo(int x)() { import std.stdio; writeln(x); } void main() { import std.typetuple; foreach (x; TypeTuple!(1, 2, 3)) foo!x(); } Notice that the loop variable x is used as a template parameter at compile time. The code expands to: foo!1(); foo!2(); foo!3();void exec(string command)(){ foreach(str ; choice.splitter(".")){ writeln(str); } }I'd like to take the opportunity to say how much I'd love to be able to do a static foreach rather than use recursion.
Jan 29 2014
On Wednesday, 29 January 2014 at 22:16:57 UTC, Peter Alexander wrote:On Wednesday, 29 January 2014 at 21:18:06 UTC, Etienne wrote:Two problems: 1) You can't use `foreach` outside functions. That means that you write: struct Foo{ foreach(i;TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } } 2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);You can use TypeTuple for static foreach. void foo(int x)() { import std.stdio; writeln(x); } void main() { import std.typetuple; foreach (x; TypeTuple!(1, 2, 3)) foo!x(); } Notice that the loop variable x is used as a template parameter at compile time. The code expands to: foo!1(); foo!2(); foo!3();void exec(string command)(){ foreach(str ; choice.splitter(".")){ writeln(str); } }I'd like to take the opportunity to say how much I'd love to be able to do a static foreach rather than use recursion.
Jan 30 2014
On Thursday, 30 January 2014 at 11:19:33 UTC, Idan Arye wrote:On Wednesday, 29 January 2014 at 22:16:57 UTC, Peter Alexander wrote:1) You can use mixin(format("<Prolog>%(<Begin>%s<End>%)<Epilog>", CompileTimeRange)); See std.format for ranges and std.string.format. 2) no. This should work for compile time foreach and TypeTuples. There are many examples in source code of Phobos.On Wednesday, 29 January 2014 at 21:18:06 UTC, Etienne wrote:Two problems: 1) You can't use `foreach` outside functions. That means that you write: struct Foo{ foreach(i;TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } } 2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);You can use TypeTuple for static foreach. void foo(int x)() { import std.stdio; writeln(x); } void main() { import std.typetuple; foreach (x; TypeTuple!(1, 2, 3)) foo!x(); } Notice that the loop variable x is used as a template parameter at compile time. The code expands to: foo!1(); foo!2(); foo!3();void exec(string command)(){ foreach(str ; choice.splitter(".")){ writeln(str); } }I'd like to take the opportunity to say how much I'd love to be able to do a static foreach rather than use recursion.
Jan 30 2014
On 01/30/2014 03:08 PM, Ilya Yaroshenko wrote:The following code fails to compile: import std.typetuple, std.stdio; void main(){ foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3); }2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);... 2) no. This should work for compile time foreach and TypeTuples. There are many examples in source code of Phobos.
Jan 30 2014
On 2014-01-30 10:28 AM, Timon Gehr wrote:import std.typetuple, std.stdio; void main(){ foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3); }This written as a static foreach or declarative foreach, would be awesome if it exposed the scope. For now the only solution is to build a string and mixin the string like this string fct(R)(){ string str; foreach (range ; R) str ~= "int num " ~ range.stringof ~ ";"; return str; } mixin(fct!(TypeTuple!(1,2,3)); writeln(num1,num2, num3); Looks a little less convenient than static foreach ( i; 0..3 ) mixin("int num" ~ i.stringof ~ ";"); writeln(num1,num2, num3);
Jan 30 2014
On Thursday, 30 January 2014 at 17:12:51 UTC, Etienne wrote:On 2014-01-30 10:28 AM, Timon Gehr wrote:It works now: mixin(format("%(int num%s; %);", [1,2,3])); //<---- writeln(num1,num2,num3);import std.typetuple, std.stdio; void main(){ foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3); }This written as a static foreach or declarative foreach, would be awesome if it exposed the scope. For now the only solution is to build a string and mixin the string like this string fct(R)(){ string str; foreach (range ; R) str ~= "int num " ~ range.stringof ~ ";"; return str; } mixin(fct!(TypeTuple!(1,2,3)); writeln(num1,num2, num3); Looks a little less convenient than static foreach ( i; 0..3 ) mixin("int num" ~ i.stringof ~ ";"); writeln(num1,num2, num3);
Jan 30 2014
On Thursday, 30 January 2014 at 15:28:34 UTC, Timon Gehr wrote:On 01/30/2014 03:08 PM, Ilya Yaroshenko wrote:I am wrong, foreach always has own scope foreach(S; TypeTuple!(string, wstring, dstring)) { import std.conv : to; S a = " a bcd ef gh "; assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")])); a = ""; assert(splitter(a).empty); } You can use mixin and format instead: import std.typetuple, std.stdio; void main(){ mixin(format("%(int num%s; %);", [1,2,3])); //<---- num1=1; num2=2; num3=3; writeln(num1,num2,num3); }The following code fails to compile: import std.typetuple, std.stdio; void main(){ foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3); }2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);... 2) no. This should work for compile time foreach and TypeTuples. There are many examples in source code of Phobos.
Jan 30 2014
On Thursday, 30 January 2014 at 17:13:21 UTC, Ilya Yaroshenko wrote:On Thursday, 30 January 2014 at 15:28:34 UTC, Timon Gehr wrote:import std.string; =)On 01/30/2014 03:08 PM, Ilya Yaroshenko wrote:I am wrong, foreach always has own scope foreach(S; TypeTuple!(string, wstring, dstring)) { import std.conv : to; S a = " a bcd ef gh "; assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")])); a = ""; assert(splitter(a).empty); } You can use mixin and format instead: import std.typetuple, std.stdio; void main(){ mixin(format("%(int num%s; %);", [1,2,3])); //<---- num1=1; num2=2; num3=3; writeln(num1,num2,num3); }The following code fails to compile: import std.typetuple, std.stdio; void main(){ foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3); }2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);... 2) no. This should work for compile time foreach and TypeTuples. There are many examples in source code of Phobos.
Jan 30 2014
On Thursday, 30 January 2014 at 14:08:27 UTC, Ilya Yaroshenko wrote:1) You can use mixin(format("<Prolog>%(<Begin>%s<End>%)<Epilog>", CompileTimeRange)); See std.format for ranges and std.string.format.That works for simple cases. Complex cases require that you write a function and calling it with CTFE for creating the mixin string. This can be a problem outside functions, because you are polluting the namespace. It might not be that bad for structs and classes, since you can simply make that helper function private - but for template mixins the helper function must be exposed to the code that uses the mixin. Having complex strings mixing also means that if there is a problem in that string, the compiler will point to the line where the string is mixed in, not the one where the code that introduces the error is concentrated to the string. Having a static foreach will make the string mixins small and simple enough for this to not be a problem. Metaprogramming by building the code strings is not something that should be encouraged(*cough* C macros *cough*), but it's currently required sometimes for complex metaprogramming in D. If we had static foreach we would still need string mixins, but at least we will be mixing in many small simple strings instead of one big complex string, and that means we could add to Phobos some helper template mixins for sanitizing the arguments for those mixins.
Jan 30 2014
Andrei has stated at least once that he agrees about usefulness / necessity of declaration foreach. It is mostly matter of someone doing implementation, same as for many other "hot" discussed stuff.
Jan 30 2014
On 1/30/14 12:33 PM, Dicebot wrote:Andrei has stated at least once that he agrees about usefulness / necessity of declaration foreach. It is mostly matter of someone doing implementation, same as for many other "hot" discussed stuff.Yah, we should have an easier means of iteratively injecting stuff into a scope. Andrei
Jan 30 2014
On Thursday, 30 January 2014 at 11:19:33 UTC, Idan Arye wrote:Two problems: 1) You can't use `foreach` outside functions. That means that you write: struct Foo{ foreach(i;TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } }mixin({ foreach(...) { ... } }())2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);You got to mixin the whole stuff, not field by field, and you won't have any issue.
Jan 30 2014
On Thursday, 30 January 2014 at 23:06:36 UTC, deadalnix wrote:On Thursday, 30 January 2014 at 11:19:33 UTC, Idan Arye wrote:Except it doesn't work inside classes and structs - it complains that "function literals cannot be class members". You have to define a named function and pollute the namespace.Two problems: 1) You can't use `foreach` outside functions. That means that you write: struct Foo{ foreach(i;TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } }mixin({ foreach(...) { ... } }())Yes, you can concatenate strings to create code. I've already answered to Ilya about that, so I'm going to copy-paste my answer: " Having complex strings mixing also means that if there is a problem in that string, the compiler will point to the line where the string is mixed in, not the one where the code that introduces the error is concentrated to the string. Having a static foreach will make the string mixins small and simple enough for this to not be a problem. Metaprogramming by building the code strings is not something that should be encouraged(*cough* C macros *cough*), but it's currently required sometimes for complex metaprogramming in D. If we had static foreach we would still need string mixins, but at least we will be mixing in many small simple strings instead of one big complex string, and that means we could add to Phobos some helper template mixins for sanitizing the arguments for those mixins. "2) `foreach` creates it's own scope. This won't work: foreach(i; TypeTuple!(1,2,3)){ mixin("int num"~i.stringof~";"); } num1=1; num2=2; num3=3; writeln(num1,num2,num3);You got to mixin the whole stuff, not field by field, and you won't have any issue.
Jan 30 2014
On 01/31/2014 03:32 AM, Idan Arye wrote:This is the corresponding issue: https://d.puremagic.com/issues/show_bug.cgi?id=7653Except it doesn't work inside classes and structs - it complains that "function literals cannot be class members". You have to define a named function and pollute the namespace.mixin({ foreach(...) { ... } }())
Jan 31 2014
On Wednesday, 29 January 2014 at 22:16:57 UTC, Peter Alexander wrote:You can use TypeTuple for static foreach. ...This is why I think using term "declaration foreach" is less confusing. It is pretty much the same but shifts attention away from essentional use case.
Jan 30 2014
On Wednesday, 29 January 2014 at 07:02:00 UTC, Andrei Alexandrescu wrote:On 1/28/14 4:14 PM, bearophile wrote:This allocates memory and as such an inferior solution. I'd prefer ugly code over this any day.This is not an enhancement request, but it presents a potential enhancement. This is C++11 code: template<int... digits> struct value; template<> struct value<> { static const int v = 0; }; template<int first, int... rest> struct value<first, rest...> { static int const v = 10 * value<rest...>::v + first; }; You can translate it to D like this: enum isInt(T) = is(T == int); template value(xs...) if (allSatisfy!(isInt, xs)) { static if (xs.length == 0) enum value = 0; else enum value = xs[0] + 10 * value!(xs[1 .. $]); }int value(int xs[]...) { int result = 0; foreach (i; xs) { result += 10 * result + i; } return result; } unittest { static assert(value(1, 2) == 21); } Andrei
Jan 29 2014
On Wednesday, 29 January 2014 at 22:25:45 UTC, inout wrote:noint value(int xs[]...) { int result = 0; foreach (i; xs) { result += 10 * result + i; } return result; } unittest { static assert(value(1, 2) == 21); } AndreiThis allocates memory
Jan 29 2014