On Tuesday, 29 August 2017 at 05:10:25 UTC, bitwise wrote:
I needed some C# style events, so I rolled my own. Long story short, the result was unsatisfactory.

[...]

Anyways, I threw together some code while thinking about what an event may look like in D:

[...]

I like the C# event syntax too and came up with the following D analogon, just to prove that a primitive library-based solution in D is doable in 35 lines and can offer as much comfort as C# here.

struct Event(Args)
{
    alias CB = void delegate(Args);
    CB[] callbacks;

    void opOpAssign(string op)(CB handler)
        if (op == "+" || op == "-")
    {
        static if (op == "+")
            callbacks ~= handler;
        else
        {
            import std.algorithm.mutation : remove;
            callbacks = callbacks.remove!(x => x == handler);
        }
    }

    void opOpAssign(string op)(void function(Args) handler)
        if (op == "+" || op == "-")
    {
        import std.functional : toDelegate;
        opOpAssign!op(toDelegate(handler));
    }

    void opCall(Args args)
    {
        foreach (cb; callbacks)
            cb(args);
    }

    bool opCast(T)()
        if (is(T == bool))
    {
        return callbacks.length != 0;
    }
}

The following test code prints the expected output:

struct S
{
    int a;
    void handler(int arg)
    {
        printf("S.handler: this.a = %d, arg = %d\n", a, arg);
    }
}

void func(int arg) { printf("func: arg = %d\n", arg); }

void main()
{
    Event!int onChanged;
    auto s = S(666);

    assert(!onChanged);

    onChanged += (int arg) { printf("lambda: arg = %d\n", arg); };
    onChanged += &func;
    onChanged += &s.handler;
    assert(onChanged);
    onChanged(1);

    onChanged -= &s.handler;
    onChanged(2);

    onChanged -= &func;
    onChanged(3);
}

Reply via email to