Thread overview
Have a template function register(T)() instantiated and executed at startup for all sub classes of a specific base class?
11 hours ago
TwoOfCups
10 hours ago
Arafel
15 minutes ago
monkyyy
6 hours ago
user1234
6 hours ago
TwoOfCups
1 hour ago
monkyyy
11 hours ago

This is a pretty open ended question here, there very well may not be any way to do what I am trying to do with the constraints I have. I am curious if anyone can think of something clever here.

So pretty much my goal here is some kind of automatic type registry for a specific set of classes. It's something like an entity component system. I need it to be a template function to do compile time reflection on the classes for serialization and things like that.

The ideal end result would be that I could simply do:

class Sub : Base {}

And Sub would be registered automatically. Primarily it is this that I am curious if there is ANY way to make happen. If I have to start inserting mixins and stuff then I can imagine all sorts of ways to make it work, but if I want the end result to be as clean as possible I can't think of a way to make it work.

Something that is so so so freakin close to working is the following:

class regTclass(T){
	pragma(crt_constructor)
	extern(C)
	static void reg(){
		writeln("Registering type ", T.stringof);
	}
}

class Base {
	this(this T)(){
		pragma(msg, "Base: Subclass is ", T.stringof);
		alias r = regTclass!(T);
	}
}

class Sub : Base {}

The above will work for one level of inheritance. An instance of regTclass!Sub will be created and the reg call will be called at start up auto magically. The flaw is it only works for one level, a class SubSub : Sub {} will not get registered.

Is there any way to resolve this flaw? Is there some other route I could take that doesn't require a mixin?

I know I can just insert a mixin and do it that way but I want things to look as clean as possible. Even that tiny amount of boiler plate bothers me a lot and this is a foundational system so that boiler plate will be multiplied hundreds of times I am expecting. Probably in the end I will just have to get over my aesthetic hang ups but I figured a forum post was worth a shot.

10 hours ago
That's something I've willing to do for a really long time too [1]. I would like to have a custom root object that automagically offers runtime introspection (given the current poor state of D in that area).

A template-this constructor will only work with one level of inheritance, as you have already discovered, and this is well known [2,3].

In my view the clean and cool solution would be to extend template this to static functions, including static constructors.

I don't think there is much opposition to that in principle, only nobody has implemented it yet (and unfortunately I don't know much about compilers, or I would have had a go at it myself).

So if you manage to make it work, please share your experience!

I think the closest I have been to this involves a mixin, and a module shared static this.

It's not perfect, but not too bad either. I'd still very much prefer to make it just as easy as inheriting from a base class.

A.

[1]: https://forum.dlang.org/post/pf8q9b$2dtb$1@digitalmars.com
[2]: https://forum.dlang.org/thread/mailman.1403.1361371073.22503.digitalmars-d@puremagic.com
[3]: https://forum.dlang.org/thread/hrxmezmyhdmkhqwbvcvi@forum.dlang.org

On 21/9/25 10:52, TwoOfCups wrote:
> This is a pretty open ended question here, there very well may not be any way to do what I am trying to do with the constraints I have. I am curious if anyone can think of something clever here.
> 
> So pretty much my goal here is some kind of automatic type registry for a specific set of classes. It's something like an entity component system. I need it to be a template function to do compile time reflection on the classes for serialization and things like that.
> 
> The ideal end result would be that I could simply do:
> 
> ```
> class Sub : Base {}
> ```
> 
> And Sub would be registered automatically. Primarily it is this that I am curious if there is **ANY** way to make happen. If I have to start inserting mixins and stuff then I can imagine all sorts of ways to make it work, but if I want the end result to be **as** clean as possible I can't think of a way to make it work.
> 
> Something that is **so so so freakin** close to working is the following:
> 
> ```
> class regTclass(T){
>      pragma(crt_constructor)
>      extern(C)
>      static void reg(){
>          writeln("Registering type ", T.stringof);
>      }
> }
> 
> class Base {
>      this(this T)(){
>          pragma(msg, "Base: Subclass is ", T.stringof);
>          alias r = regTclass!(T);
>      }
> }
> 
> class Sub : Base {}
> ```
> 
> The above will work for one level of inheritance. An instance of regTclass!Sub will be created and the reg call will be called at start up auto magically. The flaw is it only works for one level, a class SubSub : Sub {} will not get registered.
> 
> Is there any way to resolve this flaw? Is there some other route I could take that doesn't require a mixin?
> 
> I know I can just insert a mixin and do it that way but I want things to look as clean as possible. Even that tiny amount of boiler plate bothers me a lot and this is a foundational system so that boiler plate will be multiplied hundreds of times I am expecting. Probably in the end I will just have to get over my aesthetic hang ups but I figured a forum post was worth a shot.
> 
> 
> 

9 hours ago
Yeah this is on my TODO list. I first encountered these issues over 10 years ago, but couldn't begin considering how to solve them back then.

I'm waiting to see where Steven's work ends up for the GC.
That'll cover both TypeInfo and Monitor problems, that are more GC/runtime related than me.

Custom root classes, different kind of TypeInfo (subset), registration of types, reinterpretting parent methods as child instances (so have access to the actual type of this). Are all things on my list I want to explore.
6 hours ago

On Sunday, 21 September 2025 at 08:52:48 UTC, TwoOfCups wrote:

>

[...]

Is there any way to resolve this flaw? Is there some other route I could take that doesn't require a mixin?

I know I can just insert a mixin and do it that way but I want things to look as clean as possible. Even that tiny amount of boiler plate bothers me a lot and this is a foundational system so that boiler plate will be multiplied hundreds of times I am expecting. Probably in the end I will just have to get over my aesthetic hang ups but I figured a forum post was worth a shot.

You can use the CRTP pattern 0 here:

class Registerable(Derived, Base) : Base {

    pragma(crt_constructor)
    extern(C)
    static void reg(){
        writeln("Registering type ", Derived.stringof);
    }
}

class Base : Registerable!(Base, Object) {  }

class Derived1 : Registerable!(Derived1, Base) { }

class Derived2 : Registerable!(Derived2, Derived1) { }

The trick is that a a static ctor is instantiated for each instance. Note the example is a slight adaptation from link 1.

Otherwise solutions based on static intropsection will all involve mixins. Using mixins at the module level can be reasonable tho (vs using them in every new derived classes). For example:

pragma(crt_constructor)
extern(C) void reg(){
    foreach (m; __traits(allMembers, mixin(__MODULE__))){
        static if (is(mixin(m)))
            static if (is(mixin(m) : Base))
                writeln("Registering type ", m.stringof);
    }
}

class Base {}
class Sub0 : Base {}
class Sub1 : Sub0 {}

and then people just have mixin reg(), that your library provides as a q{} string, let's say.

Anyway as I think you'll like more the CRTP solution I wont develop more.

6 hours ago

On Sunday, 21 September 2025 at 13:43:46 UTC, user1234 wrote:

>

...

While both of these would work, it misses the spirit of the question. I am specifically asking if there is a way to avoid things like that.

1 hour ago

On Sunday, 21 September 2025 at 08:52:48 UTC, TwoOfCups wrote:

>

Primarily it is this that I am curious if there is ANY way to make happen.

how far into compiler bugs you wanta go?

15 minutes ago
On Sunday, 21 September 2025 at 10:15:54 UTC, Arafel wrote:
> I would like to have a custom root object that automagically offers runtime introspection

https://gist.github.com/crazymonkyyy/14e36a99dd307c5d6e37bdb3ca2524a7

Consider this direction, where you implement your introspection compile time but your `sumtype` abstraction carrys the result forward at runtime.

If you grab a compile time counter(I know of two, one is ub but its the only sensable behavoir, the other is O(n) and getting a leak of order of operations of mixins) typeinfo could be minimized to a small int + your flags; been looking into this.