cool pattern matching template

Vlad Levenfeld via Digitalmars-d digitalmars-d at puremagic.com
Wed Dec 24 14:21:16 PST 2014


Hey all, I've found myself leaning on a really versatile pattern
a lot lately, thought I'd share it: this is for those situations
where you might have something like

    static if (is (typeof(some stuff)))
      return some stuff;
    else static if (__traits(compiles, more things))
      return more things;

or perhaps some other repetitive or confusing piece of
metaprogramming logic whose goal is to decide what code to
execute.

instead, try:

    auto option_a ()() {return some stuff;}
    auto option_b ()() {return more things;}

    return Match!(option_a, option_b);

where

    template Match (T...) {
      alias Invoke (alias U) = U!();
      enum compiles (U...) = __traits(compiles, Invoke!(U[0]));
      alias Filtered = Filter!(compiles, T);

      static if (Filtered.length > 0)
        alias Match = Invoke!(Filtered[0]);
      else static assert (0);
    }

the Match template will pick the first symbol in the list that
successfully instantiates, then alias itself to the result of the
instantiation.

it can also be used to pattern-match on types, enums, or aliases:

    template MyTemp (List) {

      enum get_list_item () = List[i];
      enum get_list_temp () = List!i;

      enum MyTemp = Match!(get_list_item, get_list_temp);
    }

adding a no-op to the end of the pattern list allows Match to
fail quietly, if this is desired:

     void a ()(){some stuff;}
     void b ()(){more stuff;}
     void no_op ()() {}

     Match!(a, b, no_op);

staticMap + nested template functions can also be used to create
arbitrary runtime value tuples, which combines with Match in a
natural way:

     auto a (uint i)() {
       auto b ()() {...}
       auto c ()() {...}

       return Match!(b,c);
     }

     return func_call (staticMap!(a, staticIota!(0,n)));

supposing that func_call is an n-ary function, then this code
will call it with the best values according to the specified
matching priorities.

It can also be used to control mixin overload sets:

    template MixA () {auto func (T args) {...}}
    template MixB () {auto func (U args) {...}}

    struct MyStruct {
      mixin MixA!(...) A;
      mixin MixB!(...) B;

      auto func (V args) {
        auto a ()() {return A.func (args);}
        auto b ()() {return B.func (args);}

        return Match!(b,a);
      }
    }

here we control the routing for func by prioritizing B's func
over A's.

I've been repeatedly surprised at how many places this pattern
fits, and with how much code it has cleaned up, replacing lots of
noisy static if-else ladders and string mixins and traits logic
with some descriptively-named snippets and a call to Match.


More information about the Digitalmars-d mailing list