Static override?

Atila Neves atila.neves at gmail.com
Fri Apr 4 02:22:15 PDT 2014


For OOP we have override, which is great at preventing us from 
making mistakes by mispelling function names or using the wrong 
signature. Great.

For template, however, it's not as easy. Yes, we have template 
constraints, static if and static assert, but I found them 
wanting in certain situations. Consider this code, implementing a 
static visitor pattern:

     import std.stdio;
     import std.conv;

     enum Enum { Foo, Bar }

     enum isVisitor(T) = is(typeof(() {
         auto s = Struct();
         auto v = T();
         v.visit(s);
     }));

     enum hasAccept(T) = is(typeof(() {
         auto s = T();
         auto foo = FooVisitor();
         s.accept(foo);
         auto bar = BarVisitor();
         s.accept(bar);
     }));

     struct Struct {
         int i;
         void accept(V)(auto ref V visitor) if(isVisitor!V) {
             static if(visitor.type == Enum.Foo) {
                 writeln("accept foo");
                 visitor.visit(this);
             } else static if(visitor.type == Enum.Baz) {
                 writeln("accept bar");
                 visitor.visit(this);
             } else {
                 static assert(false, text("Unknown visitor type 
", V.stringof));
             }
         }
         static assert(hasAccept!Struct);
     }

     struct FooVisitor {
         enum type = Enum.Foo;
         void visit(T)(auto ref T t) {
             writeln("foo visiting ", t);
         }
         static assert(isVisitor!FooVisitor);
     }

     struct BarVisitor {
         enum type = Enum.Bar;
         void visit(T)(auto ref T t) {
             writeln("bar visiting ", t);
         }
         static assert(isVisitor!BarVisitor);
     }

     void main() {
         auto v = FooVisitor();
         auto s = Struct(3);
         s.accept(v);
     }


The static asserts are there to verify that the structs I define 
actually do implement the interface I want them to. Which is 
great when they work, but tricky to find out why they don't when 
the assert fails. The code above has a bug, and compiling it I 
get this:

     static_override.d(33): Error: static assert  
(hasAccept!(Struct)) is false
     Failed: ["dmd", "-v", "-o-", "static_override.d", "-I."]

It's good that it failed, but why? The problem here is that the 
lambda in hasAccept failed to compile, but the error messages the 
compiler would give me are hidden. The best I've come up with so 
far is to define this:

     mixin template assertHasAccept(T) {
         static if(!hasAccept!T) {
             void func() {
                 auto s = T();
                 auto foo = FooVisitor();
                 s.accept(foo);
                 auto bar = BarVisitor();
                 s.accept(bar);
             }
         }
     }

And then I replaced the "static assert(hasAccept!Struct)" with 
"mixin assertHasAccept!Struct", which yields this:

     static_override.d(26): Error: no property 'Baz' for type 'int'
     static_override.d(30): Error: static assert  "Unknown visitor 
type BarVisitor"
     static_override.d(66):        instantiated from here: 
accept!(BarVisitor)
     Failed: ["dmd", "-v", "-o-", "static_override.d", "-I."]

And the typo Bar->Baz is now revealed. I don't know if anyone 
else has given thought to this or has a better way of doing the 
above. Essentially I want a "static override" to check 
conformance to template interfaces at compile-time to avoid 
shooting myself in the foot, with helpful compiler error messages 
when I do. I've had a lot of errors from failing template 
constraints (good), but finding out _why_ was sometimes not easy. 
The compilation errors only happen at instantitation and the 
"is(typeof..." checks hide the errors.

Atila


More information about the Digitalmars-d mailing list