July 23, 2020
On Thursday, 23 July 2020 at 03:50:03 UTC, Manu wrote:
> That's clever, and I wish I had have thought of it before!!

Yeah, in the past my thought was so much on `mixin` this particular thing never came to mind. But last night, I was trying to goof around with no-runtime classes again (actually very easy to do basics nowadays, I'll probably blog about this monday) and wanted to experiment with dynamic casts and this came up... and I was just like "omg if the virtual slot got a template instance it would rock".

And thus it came to mind.
July 23, 2020
On Thursday, 23 July 2020 at 08:46:54 UTC, Arafel wrote:
> Specifically, I want to be able to transform Compile-time introspection into run-time instrospection:

That's a possible use case of this too, depending on how it is implemented. You might be able to make an interface method that includes a static initializer.

interface Reflectable {
    virtual void callMethod(this This)(string name) {
            // normal CT reflection based impl
    }
}

Then any

class A : Reflectable {}

will automatically implement the callMethod interface thanks to the virtual template.


Also MIGHT be possible to auto-register:

    virtual template reflect(this This) {
        // and maybe for registering a factory function...
        shared static this() { /* register */ }
    }


but that's a lot iffier because a long-form template like that may have multiple members and muddy up the interface. Still worth considering though.


In the past, proposals on this area have been along the lines of the parent class/interface defining a mixin template that the compiler automatically mixes into its child classes. That would definitely enable the auto-registration, among other things. That's actually a strict superset of my new idea and I'd be happy with it too, just this simpler one might have advantages in its simplicity; it certainly feels natural to write.
July 23, 2020
On Thursday, 23 July 2020 at 10:18:34 UTC, Arafel wrote:
> This can obviously not be resolved at compile time (where the template `this` parameter works).
>
> For runtime resolution you can already use `typeid(this)`, and this (this time yes) works:

Right. Part of my drive here is to provide features typeid currently doesn't; if you can auto-instantiate a template in child classes to implement a normal virtual interface, then we can bridge the CT and RT worlds very, very easily and create new things as-needed.
July 23, 2020
On Thursday, 23 July 2020 at 14:41:06 UTC, Adam D. Ruppe wrote:
> On Thursday, 23 July 2020 at 03:50:03 UTC, Manu wrote:
>> That's clever, and I wish I had have thought of it before!!
>
> Yeah, in the past my thought was so much on `mixin` this particular thing never came to mind. But last night, I was trying to goof around with no-runtime classes again (actually very easy to do basics nowadays, I'll probably blog about this monday) and wanted to experiment with dynamic casts and this came up... and I was just like "omg if the virtual slot got a template instance it would rock".
>
> And thus it came to mind.

FWIW, a more general way to initialize an object with static type data would be (currently it fails for indirectly derived classes due to a compiler bug):

class Base {
    this(this This)() {
        _serialize = function(Base base) {
            // base can be safely reinterpreted into This here if needed

            return This.stringof; // just for example
        };
    }

    final string serialize() {
        return _serialize(this);
    }

    private string function(Base) _serialize;
}

class Derived: Base {
}

void main() {
    Base b = new Derived;
    assert(b.serialize() == "Derived");
}

Also, there seems to be no good reason "this T" should not work for any class member, including class static constructors.
July 23, 2020
On 23/7/20 17:00, Max Samukha wrote:
> WIW, a more general way to initialize an object with static type data would be (currently it fails for indirectly derived classes due to a compiler bug):
> 
> class Base {
>      this(this This)() {
>          _serialize = function(Base base) {
>              // base can be safely reinterpreted into This here if needed
> 
>              return This.stringof; // just for example
>          };
>      }
> 
>      final string serialize() {
>          return _serialize(this);
>      }
> 
>      private string function(Base) _serialize;
> }
> 
> class Derived: Base {
> }
> 
> void main() {
>      Base b = new Derived;
>      assert(b.serialize() == "Derived");
> }
> 

Good that I read all the replies before hitting "send", because I was going to propose exactly this :-)

My only doubt is how it would interact with constructors defined in the derived classes. I think I tried doing something like this some time ago, but I couldn't make it work in the end because the `super` constructor wasn't being called.

Being able to register classes when there are first instantiated would be a useful workaround.

> Also, there seems to be no good reason "this T" should not work for any class member, including class static constructors.

This seems to be regularly reported as a bug, I posted earlier some of them. I would really love to see this work, and not just for static functions, also for enums, aliases or actually any other kind of template.
July 23, 2020
On 7/22/20 10:38 PM, Adam D. Ruppe wrote:

> But imagine if `serialize` actually got a virtual table entry, just like if it wasn't a template at all, but each child class automatically got an instance of it made for itself.

This is a great idea. Fits right in with D's philosophy of having the compiler write code for you so you don't have to.

I can imagine some great uses for serialization.

The syntax is probably the trickiest part of this potential feature -- especially since templates are automatically final, and we have no virtual keyword.

Potentially as well, you could specify overrides of the Base template in a derived type if needed.

One other tricky thing is that the override functions must not vary the parameter or return types (except for covariance).

-Steve
July 23, 2020
On Thursday, 23 July 2020 at 15:00:59 UTC, Max Samukha wrote:
> FWIW, a more general way to initialize an object with static type data would be (currently it fails for indirectly derived classes due to a compiler bug):

Yeah, I've tried that in the past too, it has a couple limitations (even if it worked):

1) it would only run at the construction site, meaning it must actually store data with the instance instead of just returning a new data in a method. This could be as simple as storing a pointer to some static data in the instance but it must be something.

2) it might not go all the way down. Imagine:

class Grandchild : Derived {}

there the implicit constructor for Derived would be called that can satisfy Base... but Grandchild may not be seen. Of course the compiler could require an explicit call to the (this This) one too though.


> Also, there seems to be no good reason "this T" should not work for any class member, including class static constructors.

indeed.
July 23, 2020
On Thursday, 23 July 2020 at 16:06:03 UTC, Steven Schveighoffer wrote:
> Potentially as well, you could specify overrides of the Base template in a derived type if needed.

Right, I'd want to do that with a normal `override` definition just like any other method.

(semantically, the virtual template would probably work the same as a mixin template. If you provide your own override, it just ignores the template version using yours instead.)

> One other tricky thing is that the override functions must not vary the parameter or return types (except for covariance).

Yeah, it would still be subject to the same semantic checks as if it was written by hand and would need to error there; it is still an interface method. Though the compiler might not actually diagnose that error until you actually try to subclass it (again, just like the mixin template thing, just without the explicit `mixin X`).
July 23, 2020
On 7/23/20 1:09 PM, Adam D. Ruppe wrote:
>> One other tricky thing is that the override functions must not vary the parameter or return types (except for covariance).
> 
> Yeah, it would still be subject to the same semantic checks as if it was written by hand and would need to error there; it is still an interface method. Though the compiler might not actually diagnose that error until you actually try to subclass it (again, just like the mixin template thing, just without the explicit `mixin X`).

It might be trickier than that. It might be that you have to ENSURE that there's only one version of the function to override. Otherwise, you can have an unbounded requirement for the vtable.

e.g.:

void foo(this This, T)(T item) {... }

As this is valid today, we can't (and shouldn't) disallow it. But it also can't be virtual.

It means we might have to require a separate syntax for this feature. An actual virtual keyword would be super-useful here.

-Steve
July 23, 2020
On Thursday, 23 July 2020 at 17:40:50 UTC, Steven Schveighoffer wrote:
> It means we might have to require a separate syntax for this feature. An actual virtual keyword would be super-useful here.

Yeah, the "virtual" keyword in my example was intentional for exactly this reason; it should create one and only one virtual table slot. Though the specific syntax of course might be different, I try not to get too worked up over that (though omg yes virtual keyword would be very useful in other places too).