Call method if declared only

Simen Kjærås simen.kjaras at gmail.com
Fri Feb 28 09:12:38 UTC 2020


On Friday, 28 February 2020 at 06:12:37 UTC, Виталий Фадеев wrote:
> Searching solution for idea !

For whatever reason, it seems my attempts at answering this 
earlier has disappeared into the void. Here:

import core.sys.windows.windows;
import std.stdio;

class Base {
     LRESULT On(UINT message, WPARAM wParam, LPARAM lParam) {
         switch (message) {
             case WM_KEYDOWN:
                 return tryCall!"OnWM_KEYDOWN"(wParam, lParam);
             default:
         }
         return 0;
     }

     auto tryCall(string name, Args...)(Args args) {
         import std.meta;

         alias This = typeof(this);
         alias module_ = __traits(parent, This);

         enum isSubclass(T...) = is(T[0] : This);
         alias getModuleMember(string name) = __traits(getMember, 
module_, name);
         enum hasMethod(T) = __traits(hasMember, T, name);

         // Get every member in this module
         enum memberNames = __traits(allMembers, module_);
         alias members = staticMap!(getModuleMember, memberNames);

         // Filter away anything that isn't derived from Base
         alias subclasses = Filter!(isSubclass, members);

         // Get rid of anything that doesn't have a method with 
the correct name
         alias subclassesWithMethod = Filter!(hasMethod, 
subclasses);

         // Sort them so you get the most derived types first
         alias Types = DerivedToFront!subclassesWithMethod;

         // Check for each type if the `this` is an instance of 
that specific one
         static foreach (T; Types) {
             if (cast(T)this !is null) {
                 // And look up that method and call it.
                 return __traits(getMember, cast(T)this, 
name)(args);
             }
         }

         // If `this` is not one of the types with that method, 
return some default value
         return 0;
     }
}

class Button : Base {
     LRESULT OnWM_KEYDOWN(WPARAM wParam, LPARAM lParam) {
         writeln("WM_KEYDOWN");
         return 0;
     }
}

unittest {
     Base b1 = new Base();
     Base b2 = new Button();

     writeln("Base:");
     b1.On(WM_KEYDOWN, 0, 0);
     writeln("Button:");
     b2.On(WM_KEYDOWN, 0, 0);
}

Now, this only works for subclasses defined in the same module. A 
possibly better solution would be interfaces:

import core.sys.windows.windows;
import std.stdio;

class Base {
     LRESULT On(UINT message, WPARAM wParam, LPARAM lParam) {
         switch (message) {
             case WM_KEYDOWN:
                 if (cast(IKeyDown)this) {
                     return 
(cast(IKeyDown)this).OnWM_KEYDOWN(wParam, lParam);
                 }
             default:
         }
         return 0;
     }
}

interface IKeyDown {
     LRESULT OnWM_KEYDOWN(WPARAM wParam, LPARAM lParam);
}

class Button : Base, IKeyDown {
     LRESULT OnWM_KEYDOWN(WPARAM wParam, LPARAM lParam) {
         writeln("WM_KEYDOWN");
         return 0;
     }
}

unittest {
     Base b1 = new Base();
     Base b2 = new Button();

     writeln("Base:");
     b1.On(WM_KEYDOWN, 0, 0);
     writeln("Button:");
     b2.On(WM_KEYDOWN, 0, 0);
}

--
   Simen


More information about the Digitalmars-d-learn mailing list