Working with generics (entities and components)

Hi all,

I hope everyone’s doing ok in their respective lockdowns!

I’m trying to implement Entities and Components but I’m struggling to understand how generics work in Haxe. This is what I’ve got:

Component

class Component {}
class ExampleComponent extends Component {
    var thing: Int; 
    public function new() {
        thing = 42;    
    }
}

Entity

class Entity {
    var components: Map<String, Component>;

    public function new() {
        components = new Map();
    }

    @:generic
    public function addComponents<C: Component>(components: Array<C>) {
        for (component in components) {
            var type = Type.getClass(component);
            var name = Type.getClassName(type);
            this.components.set(name, component);
        }
    }

    @:generic
    public function getComponent<C: Component>(type: Class<C>): Component {
        var name = Type.getClassName(type);
        var component = this.components.get(name);
        return component;
    }
}

(Any feedback on the general state of the above is much appreciated!)

What I’d like to be able to do is cast the component in getComponent so I can return the type requested. Is that possible? In C# I could do it like this:

    public C GetComponent<C>() where C : IComponent
    {
        Components.TryGetValue(typeof(C), out IComponent component);
        return (C)component;
    }

where Components is Dictionary<Type, IComponent> Components { get; set; }.

Thanks!

Hello!

First of all, @:generic doesn’t seem to be needed here. What it will do is generate a function specialization for every C, which doesn’t bring anything in this case.

Now onto the actual question: your Haxe code is very similar to C# code here, except for one small thing: in C# you cast the component ((C)) before returning it, and in Haxe you don’t - that’s why it fails: the component value is typed as Component, but the required return type should be C, which might be something else (a subclass), so an explicit cast is needed. If you remove (C) from the C# code you’ll also get a compilation error.

So, since the type is well-known here, it’s just the compiler is not smart enough to figure it out, just use an unsafe cast (return cast component) and it should work.

1 Like

Amazing! Thank you.

If I may, is the way I’m mapping the class name to a component a sensible way of doing things?

It seems quite sensible to me. You could avoid Type API usage with some macro magic, but I would only do that if it’s a performance concern or, well just for fun :).

1 Like

Great, thanks!