View mode: basic / threaded / horizontal-split · Log in · Help
October 22, 2012
Mixin replacement for switch...case?
Hi!

This is a question from a complete newbie.

Is there a way to replace switch...case statements by a mixin 
template, maybe a variadic mixin template (does such a thing 
exist?).

What I would want to achieve is to have this kind of syntax:

mixin Select!(value,
  if0, { then0(); },
  if1, { then1(); },
  if2, { foo(); bar(); },
  { thenDefault(); }
);

to replace this:

switch(value) {
  case if0 : { then0(); } break;
  case if1 : { then1(); } break;
  case if2 : { foo(); bar(); } break;
  default : thenDefault();
}

The reason I ask this is because I almost never use fall through 
and the verbosity of the switch statement has been driving me 
crazy.
October 23, 2012
Re: Mixin replacement for switch...case?
No answer. Should I assume that it is not possible?
That's something that could be done in C with a simple macro. I 
really would like to know to what extent mixins are a replacement 
for C macros for generating boilerplate code.
October 23, 2012
Re: Mixin replacement for switch...case?
OK. I have done my homework and answered my own question based on 
the Duff's Device example in the Language Reference page for 
Mixins.

The solution (not variadic though) would be:

mixin template Select!(alias value,
  alias if0, alias then0,
  alias if1, alias then1,
  alias if2, alias then2,
  alias thenDefault)
{
  switch(value) {
    case if0 : { then0(); } break;
    case if1 : { then1(); } break;
    case if2 : { then2(); } break;
    default : thenDefault();
  }
}

and it is used this way:

mixin Select!(value,
  if0, delegate { then0(); },
  if1, delegate { then1(); },
  if2, delegate { foo(); bar(); },
  delegate { thenDefault(); }
);

no gain at all verbosity-wise I'm afraid... nevermind.
October 23, 2012
Re: Mixin replacement for switch...case?
I think this should be possible, look for eg. to std.bitmanip 
bitfields template

On Tuesday, 23 October 2012 at 09:47:55 UTC, Jerome wrote:
> No answer. Should I assume that it is not possible?
> That's something that could be done in C with a simple macro. I 
> really would like to know to what extent mixins are a 
> replacement for C macros for generating boilerplate code.
October 23, 2012
Re: Mixin replacement for switch...case?
On 10/23/2012 11:47 AM, Jerome wrote:
> No answer. Should I assume that it is not possible?
>

If you like to be mistaken, feel free to do that.
http://en.wikipedia.org/wiki/Non_sequitur_%28logic%29

> That's something that could be done in C with a simple macro. I really
> would like to know to what extent mixins are a replacement for C macros
> for generating boilerplate code.

Use string mixins and templates for that if you have to. They are
better suited for code generation than C macros.
If your goal is to obfuscate the program, C macros will help you more.
October 23, 2012
Re: Mixin replacement for switch...case?
On Tuesday, 23 October 2012 at 10:40:12 UTC, Daniel Kozák wrote:
> I think this should be possible, look for eg. to std.bitmanip 
> bitfields template
>
> On Tuesday, 23 October 2012 at 09:47:55 UTC, Jerome wrote:
>> No answer. Should I assume that it is not possible?
>> That's something that could be done in C with a simple macro. 
>> I really would like to know to what extent mixins are a 
>> replacement for C macros for generating boilerplate code.

Thanks Daniel! It's exactly what I was looking for.  ;-)
October 23, 2012
Re: Mixin replacement for switch...case?
> The solution (not variadic though) would be:
>
> mixin template Select!(alias value,
>   alias if0, alias then0,
>   alias if1, alias then1,
>   alias if2, alias then2,
>   alias thenDefault)
>
> {
>   switch(value) {
>     case if0 : { then0(); } break;
>     case if1 : { then1(); } break;
>     case if2 : { then2(); } break;
>     default : thenDefault();
>   }
> }

You can get something interesting a few lines of code:

template select(cases...) if (cases.length % 2 == 1)
{
   auto select(Input)(Input input)
   {
       static if (cases.length == 1) // Default case
           return cases[0]();
       else // standard case
       {
           if (input == cases[0])
               return cases[1]();
           else
               return .select!(cases[2..$])(input);
       }
   }
}


void main()
{
   // With block delegates
   alias select!(0, { writeln("Zero.");},
                 1, { writeln("One."); },
                    { writeln("Something else.");}) counter;

   counter(0);
   counter(1);
   counter(10_000);

   // With anonymous functions:
   alias select!(0, ()=> "Zero.",
                 1, ()=> "One.",
                    ()=> "Something else.") counter2;

   writeln(counter2(0));
   writeln(counter2(1));
   writeln(counter2(10_000));
}

A slightly more generic version takes predicates as first arguments,
as Lisp's cond form:

template cond(cases...) if (cases.length % 2 == 1)
{
   auto cond(Input)(Input input)
   {
       static if (cases.length == 1) // Default case
           return cases[0]();
       else // standard case
       {
           if (cases[0](input)) // The only difference with select
               return cases[1]();
           else
               return .cond!(cases[2..$])(input);
       }
   }
}

void main()
{
   alias cond!((a) => a < 0, ()=> "Negative.",
               (a) => a > 0, ()=> "Positive.",
                             ()=> "Zero.") counter3;

   writeln(counter3(-10));
   writeln(counter3(1));
   writeln(counter3(0));
}




Philippe
October 24, 2012
Re: Mixin replacement for switch...case?
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?

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?
October 24, 2012
Re: Mixin replacement for switch...case?
> 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?

Oh! I've just figured out that it is not a mixin, but a function 
template.
October 24, 2012
Re: Mixin replacement for switch...case?
On Wed, Oct 24, 2012 at 9:53 AM, Jerome <jerome.spamable@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.
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home