On Tuesday, 3 May 2022 at 13:25:14 UTC, Arafel wrote:
I'd like to do a runtime registration system myself, using a "template this" static constructor. A simple version supporting only default constructors would be:

Yeah, you can't template this a static constructor, but you can just use a static constructor. It will have to be mixed into each child class though, and the compiler won't help to remind you.

But do something along the lines of:


```d
module factory.register;

private Object function()[string] factories;

Object construct(string name) {
        if(auto f = name in factories)
                return (*f)();
        return null;
}

mixin template Register() {
        static this() {
                import factory.register;

                alias This = typeof(this);

                // bypassing private
                __traits(getMember, factory.register, "factories")
                [This.mangleof] = function Object() {
                        // you could even delegate to a static
// function if one is present, or pass arguments // etc. this impossible with Object.factory
                        return new This();
                };
        }
}
```

That code is your library. Then, to use it:


```d
import factory.register;

class MyThing {
        // you have to remember to do this in each child
        mixin Register;
}

void main() {
        auto t = new MyThing();

        // I used the mangle instead of the FQN since it
        // is easier.
        Object o = construct(typeof(t).mangleof);
        MyThing t2 = cast(MyThing) o;
        assert(t2 !is null); // assert it actually worked
}
```




Now, you can extend this a little if you're willing to add an interface too. And if you forget to register the base class, the interface method being not implemented will remind user they did something wrong, and you can runtime assert to check child classes.

Check this out:


```d
module factory.register;

private Object function()[string] factories;

Object construct(string name) {
        if(auto f = name in factories)
                return (*f)();
        return null;
}

// adding this for the assert
bool typeIsRegistered(string name) {
        return (name in factories) !is null;
}

// this interface gives runtime access to the info we need
interface Serializable {
        string typeCode() const;
}

mixin template Register() {
        // interface implementation
        override string typeCode() const {
                // casting away const for more consistent names
                alias no_const = typeof(cast() this);

                auto name = no_const.mangleof;

// a runtime check to help remind you if something not registered
                import factory.register;
assert(typeIsRegistered(name), "Type "~typeof(this).stringof~" not registered!");

                // also making sure the child class was registered
// by ensuring the runtime type is the same as the static type assert(typeid(this) == typeid(no_const), "Child class "~typeid(this).toString()~" was not registered!");

                return name;
        }

        static this() {
                import factory.register;

                alias This = typeof(this);

                // bypassing private
                __traits(getMember, factory.register, "factories")
                [This.mangleof] = function Object() {
                        // you could even delegate to a static
// function if one is present, or pass arguments // etc. this impossible with Object.factory
                        return new This();
                };
        }
}


```


And the usage:


```d

import factory.register;

class MyThing : Serializable {
        mixin Register;
}

class Child : MyThing {
        // forgot to register uh oh
        // mixin Register;
}

void main() {
        auto t = new MyThing();

        Object o = construct(typeof(t).mangleof);
        MyThing t2 = cast(MyThing) o;
        assert(t2 !is null);

        auto child = new Child();
// if we called this in the serialize function or even one of those constructors' contracts // it can verify things work by triggering the asserts back in the library implementation
        child.typeCode();
}
```



So doing things yourself gives you some control.

Reply via email to