How to get compatible symbol names and runtime typeid names for templated classes?

Adam D Ruppe destructionator at gmail.com
Tue May 3 13:57:16 UTC 2022


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.


More information about the Digitalmars-d-learn mailing list