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