www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Call method if declared only

reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
Searching solution for idea !

Goal is to get System message, dispatch/route to method !
If method implemented only !

I dream on in future write clean code of a derived widgets like 
this :

     class Base
     {
        // dispatch
        void On( message ... )
        {
            // call On<message>()
            // example: call OnKeyUp() - if method OnKeyUp() is 
exists only
        }
     }

     class Derived : Base
     {
        // method implementation
        void OnKeyUp( ... )
        {
            //
        }
     }



I tryed code like this:

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


     class Base
     {
        LRESULT On( UINT message, WPARAM wParam, LPARAM lParam )
        {
		switch ( message )
		{
			case WM_KEYDOWN:
                                 OnWM_KEYDOWN( wParam, lParam );  
//   <-- but it required declared OnWM_KEYDOWN(...) in base class 
Base. How will be without declaring in Base ?
				break;

			default: 				
		}
        }
     }


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


May be other than derived ? May be templating ?
How to implement ?
Feb 27 2020
next sibling parent reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 06:12:37 UTC, Виталий Фадеев wrote:
 Searching solution for idea !

 Goal is to get System message, dispatch/route to method !
 If method implemented only !

 I dream on in future write clean code of a derived widgets like 
 this :

     class Base
     {
        // dispatch
        void On( message ... )
        {
            // call On<message>()
            // example: call OnKeyUp() - if method OnKeyUp() is 
 exists only
        }
     }

     class Derived : Base
     {
        // method implementation
        void OnKeyUp( ... )
        {
            //
        }
     }



 I tryed code like this:

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


     class Base
     {
        LRESULT On( UINT message, WPARAM wParam, LPARAM lParam )
        {
 		switch ( message )
 		{
 			case WM_KEYDOWN:
                                 OnWM_KEYDOWN( wParam, lParam );
  //   <-- but it required declared OnWM_KEYDOWN(...) in base 
 class Base. How will be without declaring in Base ?
 				break;

 			default: 				
 		}
        }
     }


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


 May be other than derived ? May be templating ?
 How to implement ?
Now I go in ths way: import std.stdio; import std.traits; class Base { void On( alias THIS )( int message ) { static if ( __traits( hasMember, THIS, "OnKey" ) && isCallable!( __traits( getMember, THIS, "OnKey" ) ) ) { __traits( getMember, THIS, "OnKey" )(); } } void run( alias THIS )() { On!THIS( 1 ); } } class A : Base { void OnKey() { writeln( "A.OnKey()" ); } } void main() { auto a = new A(); a.run!a(); } Searching for beauty readable code...
Feb 28 2020
next sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 28 February 2020 at 08:08:59 UTC, Виталий Фадеев wrote:
 On Friday, 28 February 2020 at 06:12:37 UTC, Виталий Фадеев 
 wrote:
 Searching solution for idea !

 Goal is to get System message, dispatch/route to method !
 If method implemented only !

 I dream on in future write clean code of a derived widgets 
 like this :

     class Base
     {
        // dispatch
        void On( message ... )
        {
            // call On<message>()
            // example: call OnKeyUp() - if method OnKeyUp() is 
 exists only
        }
     }

 May be other than derived ? May be templating ?
 How to implement ?
Now I go in ths way:
[snip]
 Searching for beauty readable code...
Here's an attempt. It looks up all subclasses of Base in the same module, and calls the named method if it exists. However, it will fail for any subclass of Base that is defined in a different module. 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); } -- Simen
Feb 28 2020
prev sibling parent reply mipri <mipri minimaltype.com> writes:
On Friday, 28 February 2020 at 08:08:59 UTC, Виталий Фадеев wrote:
 Searching for beauty readable code...
The pattern throughout Phobos is static tests, like isInputRange!R So something like this: import std.stdio; import std.traits; template canOnKey(T) { static if (__traits(hasMember, T, "onKey")) enum canOnKey = isCallable!(__traits(getMember, T, "onKey")); else enum canOnKey = false; } struct A { void onKey() { writeln("called A"); } } struct B { } struct C { bool onKey = false; } void maybeKey(T)(T o) if (canOnKey!T) { o.onKey(); } void maybeKey(T)(T o) if (!canOnKey!T) { } void main() { auto a = A(), b = B(), c = C(); maybeKey(a); maybeKey(b); maybeKey(c); } output: called A (and no other output) Of course in this exact instance it would be simpler to write void maybeKey(T)(T o) { static if (canOnKey!T) o.onKey(); } And rather than lots of specific hasThisExactMethod!T tests, it might be nice if in your actual program there's a 'class' of grouped properties that you can test for all at once, again like isInputRange!T and friends.
Feb 28 2020
parent =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 08:36:53 UTC, mipri wrote:
 On Friday, 28 February 2020 at 08:08:59 UTC, Виталий Фадеев 
 wrote:
 [...]
The pattern throughout Phobos is static tests, like isInputRange!R [...]
It cool ! I will try ! Thank you !
Feb 28 2020
prev sibling next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Feb 28 2020
parent reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 09:12:38 UTC, Simen Kjærås wrote:
 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
Yes. Thank ! I read it. Problem is - OS has many messages + user messages... It mean what interfaces like IKeyDown must me declared. All. Dream on write less code... Some like code generation. In message dispatcher. Like this: import core.sys.windows.windows; import std.stdio; import std.traits; template canOn( T, M ) { enum string callbackName = "On" ~ __traits( identifier, M ); static if ( __traits( hasMember, T, callbackName ) ) enum canOn = isCallable!( __traits( getMember, T, callbackName ) ); else enum canOn = false; } void maybeOn( T, alias M )( T o, M message ) { enum string callbackName = "On" ~ __traits( identifier, M ); static if ( canOn!(T, M) ) __traits( getMember, T, callbackName )(); } void On( T, alias M )( T o, M message ) { o.maybeOn( message ); } class Base { void Send( T, alias M )( T o, M message ) { // generate code in dispatcher // if ( canOn!( T, M ) ) // writeCode( // q{ // if ( message == WM_KEYUP ) // o.OnWM_KEYUP(); // } // ); o.On( message ); } } class A : Base { void OnWM_KEYUP() { writeln( "A.OnKey()" ); } } void main() { auto a = new A(); a.Send( a, WM_KEYUP ); } Here I have compile error... at void On( T, alias M )( T o, M message ) Now I reading Template doc for up skill... Thank!
Feb 28 2020
next sibling parent drug <drug2004 bk.ru> writes:
On 2/28/20 12:25 PM, Виталий Фадеев wrote:

 
 Now I reading Template doc for up skill...
 
I recommend this nice tutorial https://github.com/PhilippeSigaud/D-templates-tutorial
Feb 28 2020
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 28 February 2020 at 09:25:58 UTC, Виталий Фадеев wrote:
 Yes. Thank !
 I read it.
 Problem is - OS has many messages + user messages... It mean 
 what interfaces like IKeyDown must me declared. All. Dream on 
 write less code...
So let's create a template for that: interface IMessageHandler(alias msg) { mixin("LRESULT On"~__traits(identifier, msg)~"(WPARAM wParam, LPARAM lParam);"); } And use it: import core.sys.windows.windows; import std.stdio; class Base { LRESULT On(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_KEYDOWN: if (auto that = cast(IMessageHandler!WM_KEYDOWN)this) { return that.OnWM_KEYDOWN(wParam, lParam); } break; default: } return 0; } } class Button : Base, IMessageHandler!WM_KEYDOWN { 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); } You'll still have to specify for each derived class which messages they handle, but no need to define hundreds of interfaces separately. -- Simen
Feb 28 2020
parent reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 09:49:53 UTC, Simen Kjærås wrote:
 On Friday, 28 February 2020 at 09:25:58 UTC, Виталий Фадеев 
 wrote:
 [...]
So let's create a template for that: [...]
Cool!
Feb 28 2020
parent reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 10:14:41 UTC, Виталий Фадеев wrote:
 On Friday, 28 February 2020 at 09:49:53 UTC, Simen Kjærås wrote:
 On Friday, 28 February 2020 at 09:25:58 UTC, Виталий Фадеев 
 wrote:
 [...]
So let's create a template for that: [...]
Cool!
Think! I writing code generation. Like this: void On( T, alias M )( T o ) { // generate code in dispatcher // get AllMembers() // filter OnABC (ex: OnWM_KEYUP) // on each // writeCode( // q{ // if ( message == WM_KEYUP ) // o.OnWM_KEYUP(); // } // ); static ... foreach( ) ... { CB_MESSAGE = ... CB_METHOD = ... mixin ( "if ( message == CB_MESSAGE ) " ~ "o." ~ CB_METHOD ~ "();" ); } }
Feb 28 2020
parent reply =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 10:20:03 UTC, Виталий Фадеев wrote:
 On Friday, 28 February 2020 at 10:14:41 UTC, Виталий Фадеев 
 wrote:
 On Friday, 28 February 2020 at 09:49:53 UTC, Simen Kjærås 
 wrote:
 On Friday, 28 February 2020 at 09:25:58 UTC, Виталий Фадеев 
 wrote:
 [...]
So let's create a template for that: [...]
Cool!
Think! I writing code generation. Like this: void On( T, alias M )( T o ) { // generate code in dispatcher // get AllMembers() // filter OnABC (ex: OnWM_KEYUP) // on each // writeCode( // q{ // if ( message == WM_KEYUP ) // o.OnWM_KEYUP(); // } // ); static ... foreach( ) ... { CB_MESSAGE = ... CB_METHOD = ... mixin ( "if ( message == CB_MESSAGE ) " ~ "o." ~ CB_METHOD ~ "();" ); } }
Thanks all ! I happy ! Check this one: import core.sys.windows.windows; import std.stdio; import std.traits; import std.algorithm.searching; void On( T, M )( T o, M message ) { // generate code in dispatcher // get AllMembers() // filter OnABC (ex: OnWM_KEYUP) // on each // writeCode( // q{ // if ( message == WM_KEYUP ) // o.OnWM_KEYUP(); // } // ); foreach( methodName; __traits( allMembers, T ) ) { static if ( methodName.startsWith( "On" ) && methodName.length >= 3 ) { mixin ( "if ( message == " ~ methodName[2..$] ~ " ) " ~ "o." ~ methodName ~ "();" ); } } } class Base { void Send( T, M )( T o, M message ) { o.On!( T, M )( message ); } } class A : Base { void OnWM_KEYUP() { writeln( "A.OnWM_KEYUP()" ); } void OnWM_KEYDOWN() { writeln( "A.OnWM_KEYDOWN()" ); } } void main() { auto a = new A(); a.Send( a, WM_KEYUP ); a.Send( a, WM_KEYDOWN ); }
Feb 28 2020
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 28 February 2020 at 10:33:11 UTC, Виталий Фадеев wrote:
 Thanks all !
 I happy !
 Check this one:
     void On( T, M )( T o, M message )
     {
[snip]
     void main()
     {
         auto a = new A();
         a.Send( a, WM_KEYUP );
         a.Send( a, WM_KEYDOWN );
     }
That does mostly work, but fails for this code: void main() { Base a = new A(); a.send( a, WM_KEYUP ); } Basically, whenever you assign a derived to a base, this solution doesn't work, because T will be Base, not A. I enjoyed with the previous code, so I wrote the code necessary to handle any WM_ message: import core.sys.windows.windows; import std.stdio; import std.meta; template startsWith(string prefix) { enum startsWith(string s) = s.length >= prefix.length && s[0..prefix.length] == prefix; } enum getMessageValue(string s) = __traits(getMember, core.sys.windows.winuser, s); // Get the all the WM_ messages alias messageNames = Filter!(startsWith!"WM_", __traits(allMembers, core.sys.windows.winuser)); alias messageValues = NoDuplicates!(staticMap!(getMessageValue, messageNames)); class Base { LRESULT On(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { foreach (msg; messageValues) { case msg: if (auto that = cast(IMessageHandler!msg)this) { return that.handle(wParam, lParam); } break; } default: } return 0; } } interface IMessageHandler(alias msg) { mixin("LRESULT On"~__traits(identifier, msg)~"(WPARAM wParam, LPARAM lParam);"); alias handle = mixin("On"~__traits(identifier, msg)); } class Button : Base, IMessageHandler!WM_KEYDOWN, IMessageHandler!WM_SETTINGCHANGE { LRESULT OnWM_KEYDOWN(WPARAM wParam, LPARAM lParam) { writeln("WM_KEYDOWN"); return 0; } LRESULT OnWM_SETTINGCHANGE(WPARAM wParam, LPARAM lParam) { writeln("WM_SETTINGCHANGE"); return 0; } } unittest { Base b1 = new Base(); Base b2 = new Button(); writeln("Base:"); // None of these will print anything, as Base doesn't handle them b1.On(WM_KEYDOWN, 0, 0); b1.On(WM_SETTINGCHANGE, 0, 0); b1.On(WM_DRAWITEM, 0, 0); writeln("Button:"); b2.On(WM_KEYDOWN, 0, 0); b2.On(WM_SETTINGCHANGE, 0, 0); // This will print nothing, as Button doesn't handle that message. b2.On(WM_DRAWITEM, 0, 0); } -- Simen
Feb 28 2020
parent =?UTF-8?B?0JLQuNGC0LDQu9C40Lkg0KTQsNC0?= =?UTF-8?B?0LXQtdCy?= writes:
On Friday, 28 February 2020 at 12:21:41 UTC, Simen Kjærås wrote:
 On Friday, 28 February 2020 at 10:33:11 UTC, Виталий Фадеев 
 wrote:
 Thanks all !
 I happy !
 Check this one:
     void On( T, M )( T o, M message )
     {
[snip]
     void main()
     {
         auto a = new A();
         a.Send( a, WM_KEYUP );
         a.Send( a, WM_KEYDOWN );
     }
That does mostly work, but fails for this code: void main() { Base a = new A(); a.send( a, WM_KEYUP ); } Basically, whenever you assign a derived to a base, this solution doesn't work, because T will be Base, not A. I enjoyed with the previous code, so I wrote the code necessary to handle any WM_ message: import core.sys.windows.windows; import std.stdio; import std.meta; template startsWith(string prefix) { enum startsWith(string s) = s.length >= prefix.length && s[0..prefix.length] == prefix; } enum getMessageValue(string s) = __traits(getMember, core.sys.windows.winuser, s); // Get the all the WM_ messages alias messageNames = Filter!(startsWith!"WM_", __traits(allMembers, core.sys.windows.winuser)); alias messageValues = NoDuplicates!(staticMap!(getMessageValue, messageNames)); class Base { LRESULT On(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { foreach (msg; messageValues) { case msg: if (auto that = cast(IMessageHandler!msg)this) { return that.handle(wParam, lParam); } break; } default: } return 0; } } interface IMessageHandler(alias msg) { mixin("LRESULT On"~__traits(identifier, msg)~"(WPARAM wParam, LPARAM lParam);"); alias handle = mixin("On"~__traits(identifier, msg)); } class Button : Base, IMessageHandler!WM_KEYDOWN, IMessageHandler!WM_SETTINGCHANGE { LRESULT OnWM_KEYDOWN(WPARAM wParam, LPARAM lParam) { writeln("WM_KEYDOWN"); return 0; } LRESULT OnWM_SETTINGCHANGE(WPARAM wParam, LPARAM lParam) { writeln("WM_SETTINGCHANGE"); return 0; } } unittest { Base b1 = new Base(); Base b2 = new Button(); writeln("Base:"); // None of these will print anything, as Base doesn't handle them b1.On(WM_KEYDOWN, 0, 0); b1.On(WM_SETTINGCHANGE, 0, 0); b1.On(WM_DRAWITEM, 0, 0); writeln("Button:"); b2.On(WM_KEYDOWN, 0, 0); b2.On(WM_SETTINGCHANGE, 0, 0); // This will print nothing, as Button doesn't handle that message. b2.On(WM_DRAWITEM, 0, 0); } -- Simen
Thank! Today version is: import core.sys.windows.windows; import std.stdio; import std.traits; import std.conv : to; import std.algorithm.searching : startsWith; mixin template Eventable() { override void On( UINT message ) { // generate code in dispatcher // get AllMembers() // filter OnABC (ex: OnWM_KEYUP) // on each // writeCode( // if ( message == WM_KEYUP ) this.OnWM_KEYUP(); // ); foreach( methodName; __traits( allMembers, typeof( this ) ) ) { static if ( methodName.startsWith( "On" ) && methodName.length >= 3 ) { mixin ( "if ( message == " ~ methodName[2..$] ~ " ) " ~ "this." ~ methodName ~ "();" ); } } } } void Send( T, M )( T o, M message ) { o.On( message ); } class Base { void On( UINT message ) { writeln( "Base.On() // default" ); }; } class A : Base { mixin Eventable; void OnWM_KEYUP() { writeln( "A.OnWM_KEYUP()" ); } void OnWM_KEYDOWN() { writeln( "A.OnWM_KEYDOWN()" ); } } void main() { auto a = new A(); a.Send( WM_KEYUP ); // A auto c = cast(Base)a; // Base c.Send( WM_KEYUP ); } // output: // A.OnWM_KEYUP() // A.OnWM_KEYUP()
Feb 29 2020
prev sibling parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Friday, 28 February 2020 at 06:12:37 UTC, Виталий Фадеев wrote:
 How to implement ?
I would go for a template mixin that implements send operation in each subclass. Sine template mixin's content is declared in scope of declaration not template's module, the code inside it is aware of concrete implementation of your base class, and could introspect all methods available in it, and generate your switch statement (in send operation) for all supported messages. This would be just one line of code in each class with no complication whatsoever. Best regards, Alexandru.
Mar 01 2020