digitalmars.D - Static override?
- Atila Neves (98/98) Apr 04 2014 For OOP we have override, which is great at preventing us from
- bearophile (6/8) Apr 04 2014 Is this related?
- Atila Neves (3/11) Apr 04 2014 Sort of.
- Artur Skawina (17/25) Apr 04 2014 template hasAccept(T) {
- Atila Neves (23/53) Apr 04 2014 That gives me this:
- Artur Skawina (5/7) Apr 04 2014 Apparently it's compiler version dependent; I was testing with ~2.064.
- Dicebot (17/24) Apr 04 2014 I have lately started to favor separated asserts as opposed to
- Atila Neves (5/29) Apr 05 2014 That works too. I think I like the solution above better for now,
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
Apr 04 2014
Atila Neves:I don't know if anyone else has given thought to this or has a better way of doing the above.Is this related? http://msdn.microsoft.com/en-us/library/435f1dw2.aspx http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx Bye, bearophile
Apr 04 2014
Sort of. Atila On Friday, 4 April 2014 at 10:37:04 UTC, bearophile wrote:Atila Neves:I don't know if anyone else has given thought to this or has a better way of doing the above.Is this related? http://msdn.microsoft.com/en-us/library/435f1dw2.aspx http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx Bye, bearophile
Apr 04 2014
On 04/04/14 11:22, Atila Neves wrote:enum hasAccept(T) = is(typeof(() { auto s = T(); auto foo = FooVisitor(); s.accept(foo); auto bar = BarVisitor(); s.accept(bar); }));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.template hasAccept(T) { static iface(CT)(CT t) { auto foo = FooVisitor(); t.accept(foo); auto bar = BarVisitor(); t.accept(bar); } static if (is(typeof(iface!T))) enum hasAccept = true; else enum hasAccept = &iface!T; } [Should probably be split into three parts: 1) the if-definition, 2) the does-T-implement-the-if check, and 3) the T-must-implement-the-if assertion.] artur
Apr 04 2014
That gives me this: static_override.d(22): Error: variable static_override.Struct.hasAccept!(Struct).hasAccept had semantic errors when compiling Which is better than before but still hiding the actuall error. But this works as expected: template hasAccept(T) { static iface(CT)(CT t) { auto foo = FooVisitor(); t.accept(foo); auto bar = BarVisitor(); t.accept(bar); } static if (is(typeof(iface!T))) enum hasAccept = true; else { private static void func() { iface(T()); } } } Atila On Friday, 4 April 2014 at 10:55:53 UTC, Artur Skawina wrote:On 04/04/14 11:22, Atila Neves wrote:enum hasAccept(T) = is(typeof(() { auto s = T(); auto foo = FooVisitor(); s.accept(foo); auto bar = BarVisitor(); s.accept(bar); }));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.template hasAccept(T) { static iface(CT)(CT t) { auto foo = FooVisitor(); t.accept(foo); auto bar = BarVisitor(); t.accept(bar); } static if (is(typeof(iface!T))) enum hasAccept = true; else enum hasAccept = &iface!T; } [Should probably be split into three parts: 1) the if-definition, 2) the does-T-implement-the-if check, and 3) the T-must-implement-the-if assertion.] artur
Apr 04 2014
On 04/04/14 13:43, Atila Neves wrote:That gives me this: static_override.d(22): Error: variable static_override.Struct.hasAccept!(Struct).hasAccept had semantic errors when compilingApparently it's compiler version dependent; I was testing with ~2.064. Hiding the errors in this case makes no sense (compilation is going to fail anyway), so this might be a regression. artur
Apr 04 2014
On Friday, 4 April 2014 at 09:22:16 UTC, Atila Neves wrote:enum hasAccept(T) = is(typeof(() { auto s = T(); auto foo = FooVisitor(); s.accept(foo); auto bar = BarVisitor(); s.accept(bar); }));I have lately started to favor separated asserts as opposed to combined constraints: mixin template hasAccept(T) { static assert ( is(typeof(() { auto s = T(); })), "Can't create instance of " ~ T.stringof ); static assert ( is(typeof(FooVisitor)), "FooVisitor is not defined" ); // .. and so on } It is much less elegant and does not fix issue fundamentally but speeds up debugging for end user of the library quite a lot.
Apr 04 2014
That works too. I think I like the solution above better for now, I just have to figure out how to generalise it into a template mixin so I don't have to write it out over and over again. Atila On Friday, 4 April 2014 at 14:24:55 UTC, Dicebot wrote:On Friday, 4 April 2014 at 09:22:16 UTC, Atila Neves wrote:enum hasAccept(T) = is(typeof(() { auto s = T(); auto foo = FooVisitor(); s.accept(foo); auto bar = BarVisitor(); s.accept(bar); }));I have lately started to favor separated asserts as opposed to combined constraints: mixin template hasAccept(T) { static assert ( is(typeof(() { auto s = T(); })), "Can't create instance of " ~ T.stringof ); static assert ( is(typeof(FooVisitor)), "FooVisitor is not defined" ); // .. and so on } It is much less elegant and does not fix issue fundamentally but speeds up debugging for end user of the library quite a lot.
Apr 05 2014