On Wed, Oct 24, 2012 at 9:53 AM, Jerome <jerome.spama...@yahoo.com> wrote: > Thanks Philippe! Great solution! > > I have two remarks. > > Remark 1: I understand that your mixin will be expanded into cascaded > if...else statements. It would probably be more efficient to expand into > switch...case, don't you think?
Probably, but my solution can be generalized further, to provide a sort of pattern-matching: template match(cases...) { auto match(Input...)(Input input) { static if (cases.length == 0) static assert(false, "No match for args of type "~ Input.stringof); else static if (__traits(compiles, cases[0](input))) // Can we call cases[0] on input? return cases[0](input); // If yes, do it else // else, recurse farther down return .match1!(cases[1..$])(input); } } string more(T...)(T t){ return "More than two args. Isn't life wonderful?";} void main() { alias match!( () => "No args", (a) => "One arg, of type " ~ typeof(a).stringof ~ " with value: " ~ to!string(a), (a, string b)=> "Two args (" ~ to!string(a) ~ ", " ~ to!string(b) ~ "). I know the second one is a string.", (a, b) => "Two args", more ) matcher; writeln(matcher()); writeln(matcher(3.1416)); writeln(matcher(1, "abc")); writeln(matcher(1, 1)); writeln(matcher(1, "abc", 3.1416)); writeln(matcher(1,1,1,1,1)); } As you can see, different branches are selected based on the number and type of arguments. This is quite powerful: auto-detection based on the number of args, using the short syntax for function templates (args ) => result Only for `more` did I need to define an external function. Of course, standard (non-templated) functions can be used too. The only limitation is that all branches must return the same type, as for a stand switch... case statement. But even this can be circumvented. The code is longer, I paste is there: http://dpaste.dzfl.pl/c315a160 usage: void main() { alias match!( () => 3.14159, (a) => "One arg, of type " ~ typeof(a).stringof ~ " with value: " ~ to!string(a), (a, string b)=> "Two args (" ~ to!string(a) ~ ", " ~ to!string(b) ~ "). I know the second one is a string.", (a, b) => 0, more ) matcher; writeln(matcher()); writeln(matcher(3.1416)); writeln(matcher(1, "abc")); writeln(matcher(1, 1)); writeln(matcher(1, "abc", 3.1416)); writeln(matcher(1,1,1,1,1)); } Different argument lists, different result types! > Remark 2: I infer from your code that the "delegate" keyword is not > mandatory, so my solution could also be called like this: > > > mixin Select!(value, > if0, { then0(); }, > if1, { then1(); }, > if2, { foo(); bar(); }, > { thenDefault(); } > ); > > instead of: > > > mixin Select!(value, > if0, delegate { then0(); }, > if1, delegate { then1(); }, > if2, delegate { foo(); bar(); }, > delegate { thenDefault(); } > ); > > Is that correct? Yes, it is. code blocks are void delegate()'s in D, or T delegate() with a return statement: { writeln("Hello World!"); return 0;} is an int delegate(). You can also use the short delegate syntax: mixin Select!(value, if0, () => then0(), if1, () => then1(), if2, () => (foo(), bar()), () => thenDefault() ); Notice that, in your previous example 'value' is a compile-time value.My examples were made so as to permit runtime arguments.