I needed some C# style events, so I rolled my own. Long story short, the result was unsatisfactory.

Library based events are inadequate for basically the same reasons as library based properties (often suggested/attempted in C++). The problem is that the properties/events don't have access to the fields or methods of the containing object, and as such, incur the cost of an extra pointer per event/property, or worse, a delegate if custom behavior per event is needed, in order to provide that access. One obvious example would be synchronized properties/events.

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

struct Foo
{
    List!(void function()) callbacks;

    @event void onEvent(string op, Args...)(Args args)
    {
        static if(op == "+")
        {
            callbacks.add(args[0]);
        }
        else static if(op == "-")
        {
            callbacks.remove(args[0])
        }
        else static if(op == "()")
        {
            foreach(cb; callbacks)
                cb(args);
        }
    }

    // or..

    @event {
        void onEvent(string op, Args...)(Args args)
if(op == "+" && Args.length == 1 && isSomeFunction(Args[0]))
        {
            callbacks.add(args[0]);
        }

        void onEvent(string op, Args...)(Args args)
if(op == "-" && Args.length == 1 && isSomeFunction(Args[0]))
        {
            callbacks.remove(args[0]);
        }

        void onEvent(string op, Args...)(Args args)
if(op == "()" && __traits(compiles, { callbacks[0](args); })
        {
            foreach(cb; callbacks)
                cb(args);
        }

        // this could work in the example above
        // if events just always returned an int
        bool onEvent(string op, Args...)(Args args)
            if(op == "!!" && Args.length == 0)
        {
            return !callbacks.empty;
        }
    }
}

void baz(int n) {
    writeln(n);
}

so usage like this:

`
Foo foo;
foo.onEvent += (int n) => writeln(n);
foo.onEvent += &baz;
foo.onEvent -= &baz;

if(foo.onEvent)
    foo.onEvent(1);
`

becomes this:

`
Foo foo;
foo.onEvent!"+"(() => writeln("bar"));
foo.onEvent!"+"(&baz);
foo.onEvent!"-"(&baz);

if(foo.onEvent!"!!"())
     foo.onEvent!"()"(1);
`

and outputs this:

1

Reply via email to