digitalmars.D - Idea to verify virtual/final methods
- Jacob Carlborg (44/44) Jun 04 2013 In the thread "Slow performance compared to C++, ideas?" Manu is arguing...
- bearophile (6/16) Jun 04 2013 Is it possible to also write:
- Jacob Carlborg (6/9) Jun 04 2013 Not with this version. I tried that first but I couldn't get
- bearophile (8/16) Jun 04 2013 Or something like:
- Jacob Carlborg (38/41) Jun 04 2013 It actually works. My mistake. Here's a version that works with modules
- Manu (7/52) Jun 04 2013 This is a great idea. If final-by-default is ultimately rejected, I'll
- Jacob Carlborg (16/22) Jun 04 2013 Yes, exactly. If you override RTInfo in druntime I think it will work
- bearophile (13/23) Jun 04 2013 My original purposes for UDAs was to extend the type system, as
- Jacob Carlborg (7/17) Jun 04 2013 Absolutely. My idea for AST macros, which includes integration with
- Adam D. Ruppe (48/49) Jun 04 2013 bearophile, you're a genius. I've been trying to think of a way
- Adam D. Ruppe (11/11) Jun 04 2013 Though a downside here is you can't require a check attribute to
- Namespace (2/2) Jun 04 2013 Awesome! But I miss the filename and the line number where the
- Jacob Carlborg (4/6) Jun 04 2013 Hmm, I wonder if that's possible to get.
- Namespace (2/7) Jun 04 2013 I do not know how. But with that it would be really incredible.
- Jacob Carlborg (9/11) Jun 04 2013 template RTInfo(T, string mod = __MODULE__, string file = __FILE__,
- Namespace (2/12) Jun 04 2013 No chance without changing and rebuilding druntime?
- Jacob Carlborg (4/5) Jun 04 2013 Not that I can think of right now.
In the thread "Slow performance compared to C++, ideas?" Manu is arguing that methods should be non-virtual by default. The problem he is having is that developers will forget to declare methods as final. This issue got me think if there's a way to automatically detect that a method is virtual that shouldn't be. If one modifies druntime to take advantage out of RTInfo. If I understand everything correctly RTInfo will be instantiated once for each user defined type. With that and the help of a UDA you can check that all methods that should be final really are final. Something like: struct virtual {} // the UDA class Foo { void a () {} // static assert, not declared as virtual virtual void b () {} // ok virtual final void c () {} // static assert, declared as final and virtual final d () {} // ok } checkVirtual!(Foo); import std.typetuple; void checkVirtual (T) () { static if (!is(T == class)) return; foreach (m ; __traits(derivedMembers, T)) { alias TypeTuple!(__traits(getAttributes, mixin("T." ~ m))) attrs; enum methodName = T.stringof ~ "." ~ m.stringof; static if (staticIndexOf!(virtual, attrs) != -1) { static if (!__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " marked as virtual is not virtual"); } else { static if (__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " is virtual but not marked as virtual"); } } } -- /Jacob Carlborg
Jun 04 2013
Jacob Carlborg:struct virtual {} // the UDA class Foo { void a () {} // static assert, not declared as virtual virtual void b () {} // ok virtual final void c () {} // static assert, declared as final and virtual final d () {} // ok } checkVirtual!(Foo);Is it possible to also write: checkVirtual!myModuleName; And verify all the classes in a module? Bye, bearophile
Jun 04 2013
On 2013-06-04 12:45, bearophile wrote:Is it possible to also write: checkVirtual!myModuleName; And verify all the classes in a module?Not with this version. I tried that first but I couldn't get __traits(allMembers) to work on a module. I think by overriding RTInfo it will be more automatic. -- /Jacob Carlborg
Jun 04 2013
Jacob Carlborg:Or something like: checkVirtual!(__MODULE__);Is it possible to also write: checkVirtual!myModuleName; And verify all the classes in a module?Not with this version. I tried that first but I couldn't get __traits(allMembers) to work on a module. I think by overriding RTInfo it will be more automatic.If there are some things that don't give you enough static introspection to perform that, then it's worth adding that missing static introspection to D language. Bye, bearophile
Jun 04 2013
On 2013-06-04 13:13, bearophile wrote:If there are some things that don't give you enough static introspection to perform that, then it's worth adding that missing static introspection to D language.It actually works. My mistake. Here's a version that works with modules as well: void checkVirtual (alias T) () { static if (is(T == class)) { foreach (m ; __traits(derivedMembers, T)) { alias TypeTuple!(__traits(getAttributes, mixin("T." ~ m))) attrs; enum methodName = T.stringof ~ "." ~ m.stringof; static if (staticIndexOf!(virtual, attrs) != -1) { static if (!__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " marked as virtual is not virtual"); } else { static if (__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " is virtual but not marked as virtual"); } } } else { foreach (m ; __traits(allMembers, T)) { mixin("alias " ~ m ~ " member;"); static if (is(member == class)) checkVirtual!(member); } } } -- /Jacob Carlborg
Jun 04 2013
On 4 June 2013 18:23, Jacob Carlborg <doob me.com> wrote:In the thread "Slow performance compared to C++, ideas?" Manu is arguing that methods should be non-virtual by default. The problem he is having is that developers will forget to declare methods as final. This issue got me think if there's a way to automatically detect that a method is virtual that shouldn't be. If one modifies druntime to take advantage out of RTInfo. If I understand everything correctly RTInfo will be instantiated once for each user defined type. With that and the help of a UDA you can check that all methods that should be final really are final. Something like: struct virtual {} // the UDA class Foo { void a () {} // static assert, not declared as virtual virtual void b () {} // ok virtual final void c () {} // static assert, declared as final and virtual final d () {} // ok } checkVirtual!(Foo); import std.typetuple; void checkVirtual (T) () { static if (!is(T == class)) return; foreach (m ; __traits(derivedMembers, T)) { alias TypeTuple!(__traits(**getAttributes, mixin("T." ~ m))) attrs; enum methodName = T.stringof ~ "." ~ m.stringof; static if (staticIndexOf!(virtual, attrs) != -1) { static if (!__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " marked as virtual is not virtual"); } else { static if (__traits(isVirtualMethod, mixin("T." ~ m))) static assert (false, "Method " ~ methodName ~ " is virtual but not marked as virtual"); } } } -- /Jacob CarlborgThis is a great idea. If final-by-default is ultimately rejected, I'll definitely try this out. That said, it only solves half the problem, that is, internal programmers forgetting to mark their functions final (and assuming they also remember 'checkVirtual!' in their moules). It offers nothing to address the 3rd party library problem.
Jun 04 2013
On 2013-06-04 13:26, Manu wrote:This is a great idea. If final-by-default is ultimately rejected, I'll definitely try this out. That said, it only solves half the problem, that is, internal programmers forgetting to mark their functions final (and assuming they also remember 'checkVirtual!' in their moules). It offers nothing to address the 3rd party library problem.Yes, exactly. If you override RTInfo in druntime I think it will work automatically. Currently RTInfo looks like this: template RTInfo(T) { enum RTInfo = null; } Override it to look like this: template RTInfo(T) { enum RTInfo = checkVirtual!(T); } If this doesn't work you could create a tool which imports all modules in the source code and calls the function for all modules. -- /Jacob Carlborg
Jun 04 2013
Jacob Carlborg:struct virtual {} // the UDA class Foo { void a () {} // static assert, not declared as virtual virtual void b () {} // ok virtual final void c () {} // static assert, declared as final and virtual final d () {} // ok } checkVirtual!(Foo);My original purposes for UDAs was to extend the type system, as you have essentially done here. But to do that well enough the attributes need to be more active. An idea: struct Small(T) if (T.sizeof <= 16) {} Small struct Bar { int x; } Here Small get instantiated with T == Bar, and its constraint performs the active compile-time test. Bye, bearophile
Jun 04 2013
On 2013-06-04 15:15, bearophile wrote:My original purposes for UDAs was to extend the type system, as you have essentially done here. But to do that well enough the attributes need to be more active. An idea: struct Small(T) if (T.sizeof <= 16) {} Small struct Bar { int x; } Here Small get instantiated with T == Bar, and its constraint performs the active compile-time test.Absolutely. My idea for AST macros, which includes integration with UDA's, could possibly solve that. https://dl.dropboxusercontent.com/u/18386187/ast_macros.html See the bottom for attributes. -- /Jacob Carlborg
Jun 04 2013
On Tuesday, 4 June 2013 at 13:15:39 UTC, bearophile wrote:An idea:bearophile, you're a genius. I've been trying to think of a way to do this for the last week or so and you've got it. usage: struct small(T) if(T.sizeof < 9) {} CustomCheck!small class RTTest {} object.d additions: enum CustomCtCheckResult { fail, pass } template CustomCheck(alias C) { template CustomCheck(T) { static if(__traits(compiles, C!T)) enum CustomCheck = CustomCtCheckResult.pass; else enum CustomCheck = CustomCtCheckResult.fail; } } bool doCustomChecks(T)() { if(__ctfe) { foreach(attr; __traits(getAttributes, T)) { static if(is(typeof(attr!T) == CustomCtCheckResult)) { static assert(attr!T == CustomCtCheckResult.pass); } } return true; } } template RTInfo(T) { /* other rtinfo stuff can remain */ enum customChecksPassed = doCustomChecks!T; } If we updated this to go right through the members too, we'd be really cooking. object.d(755): Error: static assert (cast(CustomCtCheckResult)0 == cast(CustomCtCheckResult)1) is false object.d(774): instantiated from here: doCustomChecks!(RTTest) minimal.d(168): instantiated from here: RTInfo!(RTTest) the last line helps find it. For members it will be trickier but there the CustomCheck template could take __FILE__ and __LINE__ as default arguments and just print them instead of static assert 0.
Jun 04 2013
Though a downside here is you can't require a check attribute to be present on a class without modifying druntime. The class attribute could go down and check its members, but there's no way to error on an unchecked class without changing object.d, or at least a runtime check to loop through the modules and check it then. It just checks ones that are there, and at that point, it isn't really that much different than just writing static assert(test.sizeof < 8); right there in the module. so maybe not as awesome as i first thought, but still, this is an exciting technique and I'm sure there's more we can do with it.
Jun 04 2013
Awesome! But I miss the filename and the line number where the error happened.
Jun 04 2013
On 2013-06-04 15:22, Namespace wrote:Awesome! But I miss the filename and the line number where the error happened.Hmm, I wonder if that's possible to get. -- /Jacob Carlborg
Jun 04 2013
On Tuesday, 4 June 2013 at 13:25:37 UTC, Jacob Carlborg wrote:On 2013-06-04 15:22, Namespace wrote:I do not know how. But with that it would be really incredible.Awesome! But I miss the filename and the line number where the error happened.Hmm, I wonder if that's possible to get.
Jun 04 2013
On 2013-06-04 15:22, Namespace wrote:Awesome! But I miss the filename and the line number where the error happened.template RTInfo(T, string mod = __MODULE__, string file = __FILE__, size_t line = __LINE__) { enum RTInfo = checkVirtual!(T, mod, file, line); } The above could work. -- /Jacob Carlborg
Jun 04 2013
On Tuesday, 4 June 2013 at 13:44:49 UTC, Jacob Carlborg wrote:On 2013-06-04 15:22, Namespace wrote:No chance without changing and rebuilding druntime?Awesome! But I miss the filename and the line number where the error happened.template RTInfo(T, string mod = __MODULE__, string file = __FILE__, size_t line = __LINE__) { enum RTInfo = checkVirtual!(T, mod, file, line); } The above could work.
Jun 04 2013
On 2013-06-04 18:43, Namespace wrote:No chance without changing and rebuilding druntime?Not that I can think of right now. -- /Jacob Carlborg
Jun 04 2013