Search code examples
templatesgenericsdelegatesd

Dlang: How to handle an array of various types


I'm learning D and I'm playing with ways of handling generic types. In D how can I declare an array that can store delegates with varying input arguments?

In the example I'm trying to partially mimic the way JavaScript uses .addEventListener/.dispatchEvent and to do that i need to store an array of listeners where they will have various event types as input arguments. I'm trying to prevent a lot of type casting back and forth, but I'm stuck at the point where I need to store them in the same array.

    import std.stdio;

    class Event {
    }

    class NewEvent : Event {
    }

    class EventTarget {
        void addEventListener(T)(void delegate(T) listener) {
            this.listeners ~ listener;
        }

        void dispatchEvent(T)(T event) {
            foreach (listener; this.listeners) {
                writeln("listener: ", listener, " ", event);
                // TODO: call listener if it's input argument matches the T type
                // if (type is the one matching the listeners first input argument) {
                //  listener(event);
                // }
            }
        }

    private:
        // TODO: find a way to store multiple event types in this array
        void delegate(T)[] listeners;
    }

    void main() {
        auto target = new EventTarget();

        target.addEventListener((NewEvent event) {
            write("executed handler for NewEvent", event);
        });

        target.addEventListener((Event event) {
            write("executed handler for Event", event);
        });

        target.dispatchEvent(new NewEvent());
        target.dispatchEvent(new Event());
    }

There is a playground here https://run.dlang.io/is/XdTVBc


Solution

  • This one should works:

    import std.stdio;
    import std.traits;
    
    abstract class Event {
    
    }
    
    class NewEvent : Event {
    }
    
    class NewEvent2 : Event {
    }
    
    class EventTarget {
        void addEventListener(T : Event)(void delegate(T) listener) {
            this.listeners ~= DG(listener);
        }
    
        void dispatchEvent(T : Event)(T event) {
    
            foreach (listener; listeners)
            {            
                if (listener.type_name == fullyQualifiedName!T)
                {
                    listener(event);
                }
            }
        }
    
    private:    
        DG[] listeners;
    }
    
    struct DG
    {
        string type_name;
        void delegate(Event) dg;
        alias dg this;
        this(T : Event)(void delegate(T) listener)
        {
            type_name = fullyQualifiedName!T;
            dg = cast(void delegate(Event)) listener;
        }
    }
    
    void main() {
        auto target = new EventTarget();
    
        target.addEventListener((NewEvent event) {
            writeln("executed handler for NewEvent", event);
        });
    
        target.addEventListener((NewEvent2 event) {
            writeln("executed handler for Event", event);
        });
    
        target.dispatchEvent(new NewEvent());
        target.dispatchEvent(new NewEvent2());
    }