www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - [Question] About mixin, template and alias

reply "Michael" <pr m1xa.com> writes:
/////// fire.d

import std.stdio;

alias void delegate() EventHandler;

class Event(T)
{
     private T[] _events;

     public void opOpAssign(string op)(T param) if (op == "~")
     {
         writeln(param.funcptr);
         _events ~= param;
     }

     public void opCall(ParamType ...)(ParamType params)
     {
         emit(params);
     }

     protected void emit(ParamType ...)(ParamType params)
     {
         foreach (event; _events)
             event(params);
     }
}

mixin template AddEvent(DelegateType, string member)
{
     auto eventObserver =  new Event!DelegateType();

     mixin("alias eventObserver " ~ member ~ ";");
}

class Fire
{
     public mixin AddEvent!(EventHandler, "click");
}

/////// water.d
import std.stdio;

import fire;

class Water : Fire
{

}

void main()
{
     auto fire  = new Fire();
     auto water = new Water();
     water.click ~= () { writeln("Water!"); };
     fire.click  ~= () { writeln("Fire!");  };

     fire.click();
}


Output:
Water!
Fire!

Someone can explain me a behaviour of above code? Why not "Fire!" 
only?
Sep 22 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 22 September 2013 at 18:31:20 UTC, Michael wrote:
 /////// fire.d

 import std.stdio;

 alias void delegate() EventHandler;

 class Event(T)
 {
     private T[] _events;

     public void opOpAssign(string op)(T param) if (op == "~")
     {
         writeln(param.funcptr);
         _events ~= param;
     }

     public void opCall(ParamType ...)(ParamType params)
     {
         emit(params);
     }

     protected void emit(ParamType ...)(ParamType params)
     {
         foreach (event; _events)
             event(params);
     }
 }

 mixin template AddEvent(DelegateType, string member)
 {
     auto eventObserver =  new Event!DelegateType();

     mixin("alias eventObserver " ~ member ~ ";");
 }

 class Fire
 {
     public mixin AddEvent!(EventHandler, "click");
 }

 /////// water.d
 import std.stdio;

 import fire;

 class Water : Fire
 {

 }

 void main()
 {
     auto fire  = new Fire();
     auto water = new Water();
     water.click ~= () { writeln("Water!"); };
     fire.click  ~= () { writeln("Fire!");  };

     fire.click();
 }


 Output:
 Water!
 Fire!

 Someone can explain me a behaviour of above code? Why not 
 "Fire!" only?
This: //---- auto eventObserver = new Event!DelegateType(); //---- Does not do what you think it does: It *statically* initializes "eventObserver" to the *single* "new Event!DelegateType();". SO basically, all your instances are sharing the same Event. I'm surprised this compiles at all, what with Fire.init depending on a run-time call, but apparently, dmd is "smart enough" to do the allocation at compile time.
Sep 22 2013
parent reply "Michael" <pr m1xa.com> writes:
 This:
 //----
     auto eventObserver =  new Event!DelegateType();
 //----
 Does not do what you think it does: It *statically* initializes 
 "eventObserver" to the *single* "new Event!DelegateType();". SO 
 basically, all your instances are sharing the same Event.

 I'm surprised this compiles at all, what with Fire.init 
 depending on a run-time call, but apparently, dmd is "smart 
 enough" to do the allocation at compile time.
Why like "static"?
Sep 22 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 22 September 2013 at 21:18:13 UTC, Michael wrote:
 This:
 //----
    auto eventObserver =  new Event!DelegateType();
 //----
 Does not do what you think it does: It *statically* 
 initializes "eventObserver" to the *single* "new 
 Event!DelegateType();". SO basically, all your instances are 
 sharing the same Event.

 I'm surprised this compiles at all, what with Fire.init 
 depending on a run-time call, but apparently, dmd is "smart 
 enough" to do the allocation at compile time.
Why like "static"?
Because that is the definition of inline initialization. Basically, every type (struct/class) has a ".init" state, which is a compile-time known static initial value. So when you write "auto a = new A;" It means a resolves to the *value* which is the result of "new A". This is not to be confused with the *expression*. In any case, I think the code is wrong to begin with, since you are basically using a non shared to access a non-TLS global. I'm filing a bug report right now.
Sep 22 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 23 September 2013 at 06:31:01 UTC, monarch_dodra wrote:
 I'm filing a bug report right now.
http://d.puremagic.com/issues/show_bug.cgi?id=11107
Sep 22 2013
next sibling parent "Michael" <pr m1xa.com> writes:
I understand. So, at least it's has interesting behaviour and big 
question)
Sep 23 2013
prev sibling parent "Michael" <pr m1xa.com> writes:
Code below shows that I would achieve:

///// fire.d
alias void delegate() EventHandler;

class Event(T)
{
     private T[] _events;

     public void opOpAssign(string op)(T param) if (op == "~")
     {
         _events ~= param;
     }

     public void opCall(ParamType ...)(ParamType params)
     {
         this.emit(params);
     }

     protected void emit(ParamType ...)(ParamType params)
     {
         foreach (event; _events)
             event(params);
     }
}

mixin template AddEvent(DelegateType, string member)
{
     private Event!DelegateType _eventObserver;

      property auto get()
     {
         if (_eventObserver is null)
             _eventObserver = new Event!DelegateType();

         return _eventObserver;
     }

     void doEventObserver(ParamType ...)(ParamType params)
     {
         mixin (member ~ "()(params);");
     }

     mixin ("alias get " ~ member ~ ";");
     mixin ("alias doEventObserver do" ~ member ~ ";");
}

class Fire
{
     public mixin AddEvent!(EventHandler, "click");
}

class DoubleFire
{
     private Event!EventHandler _click;


     public this()
     {
         _click = new Event!EventHandler();
     }

      property public Event!EventHandler click()
     {
         return _click;
     }

     public void doClick(ParamType ...)(ParamType params)
     {
         click()(params);
     }
}


///// water.d
import std.stdio;

import fire;

class Water : Fire
{

}

void main()
{
     Fire element;

     element = new Fire();
     element.click  ~= () { writeln("Fire!");  };
     element.click()(); // Not needed with DIP 23

     element = new Water();
     element.click ~= () { writeln("Water!"); };
     element.click()(); // Not needed with DIP 23

     DoubleFire df = new DoubleFire();
     df.click ~= () { writeln("Double Fire!"); };
     df.doClick(); // Workaround

     element.doclick(); // Workaround
}

///// Output

Fire!
Water!
Double Fire!
Water!


Thanks)
Sep 23 2013