Thread overview
[Question] About mixin, template and alias
Sep 22, 2013
Michael
Sep 22, 2013
monarch_dodra
Sep 22, 2013
Michael
Sep 23, 2013
monarch_dodra
Sep 23, 2013
monarch_dodra
Sep 23, 2013
Michael
Sep 23, 2013
Michael
September 22, 2013
/////// 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?
September 22, 2013
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.
September 22, 2013
> 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"?

September 23, 2013
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.
September 23, 2013
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
September 23, 2013
I understand. So, at least it's has interesting behaviour and big question)

September 23, 2013
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)