Mixin replacement for switch...case?
Philippe Sigaud
philippe.sigaud at gmail.com
Wed Oct 24 11:19:50 PDT 2012
On Wed, Oct 24, 2012 at 9:53 AM, Jerome <jerome.spamable at 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.
More information about the Digitalmars-d
mailing list