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.

Reply via email to