Thread overview
Re: Dynamic templated virtuals - I still often want them
Mar 12, 2022
surlymoor
Mar 12, 2022
Timon Gehr
Mar 13, 2022
surlymoor
Mar 14, 2022
Walter Bright
Mar 14, 2022
Timon Gehr
Mar 12, 2022
zjh
Mar 14, 2022
bauss
Mar 14, 2022
zjh
Mar 14, 2022
bauss
March 12, 2022
On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:
> You might know about D's template this parameters, which adapt to the static type of a `this` reference on a call, or with the curiously recurring template pattern, which passes a derived class to the base class so the base class can inspect the derived class.
>
> Both of these are useful at times, but neither quite do what I have in mind here.
>
> Imagine this:
>
> ---
> class Base {
>      virtual string serialize(this This)() {
>            return This.stringof; // just for example
>      }
> }
>
> class Derived : Base {
>
> }
>
> void main() {
>     Base b = new Derived();
>     assert(b.serialize() == "Derived");
> }
> ---
>
> That does NOT work today. For one, of course, D has no `virtual` keyword, but if you left that out, it would compile, but fail the assert because the static type of `b` passed to the template This is actually still `Base`.
>
> 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.
>
> So it would be as if I wrote
>
> class Base {
>      string serialize() {
>            return Base.stringof;
>      }
> }
>
> class Derived : Base {
>      override string serialize() {
>            return Derived.stringof;
>      }
> }
>
> by hand. Of course, it is possible to do this kind of thing with mixin templates or the CRTP, but in both cases, the Derived class must actually write it in the child class, and if you don't there's no way to really tell; it will still compile and just use the base class implementation. Which might be OK but it isn't perfect.
>
> It would just be cool if the base class template was automatically instantiated again for the child class, while still working like a normal virtual call.

Man, I'd really kill for this right about now. I was designing something that depended on the proposed behavior existing. My ignorance is my own fault, of course, but still this would be a very cool feature for D.
March 12, 2022
On 3/12/22 06:10, surlymoor wrote:
> On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:
>> You might know about D's template this parameters, which adapt to the static type of a `this` reference on a call, or with the curiously recurring template pattern, which passes a derived class to the base class so the base class can inspect the derived class.
>>
>> Both of these are useful at times, but neither quite do what I have in mind here.
>>
>> Imagine this:
>>
>> ---
>> class Base {
>>      virtual string serialize(this This)() {
>>            return This.stringof; // just for example
>>      }
>> }
>>
>> class Derived : Base {
>>
>> }
>>
>> void main() {
>>     Base b = new Derived();
>>     assert(b.serialize() == "Derived");
>> }
>> ---
>>
>> That does NOT work today. For one, of course, D has no `virtual` keyword, but if you left that out, it would compile, but fail the assert because the static type of `b` passed to the template This is actually still `Base`.
>>
>> 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.
>>
>> So it would be as if I wrote
>>
>> class Base {
>>      string serialize() {
>>            return Base.stringof;
>>      }
>> }
>>
>> class Derived : Base {
>>      override string serialize() {
>>            return Derived.stringof;
>>      }
>> }
>>
>> by hand. Of course, it is possible to do this kind of thing with mixin templates or the CRTP, but in both cases, the Derived class must actually write it in the child class, and if you don't there's no way to really tell; it will still compile and just use the base class implementation. Which might be OK but it isn't perfect.
>>
>> It would just be cool if the base class template was automatically instantiated again for the child class, while still working like a normal virtual call.
> 
> Man, I'd really kill for this right about now. I was designing something that depended on the proposed behavior existing. My ignorance is my own fault, of course, but still this would be a very cool feature for D.

The way I work around this is by putting template mixins into all subclasses, using `typeof(this)`. (It's a bit finicky though, template mixins are notorious for exposing forward reference compiler bugs.)
March 12, 2022

On Saturday, 12 March 2022 at 05:10:10 UTC, surlymoor wrote:

>

On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:

>

You might know about D's template this parameters, which adapt

Each class should have a This type or information like rust's self/Self to indicate Self Type.
The base class should have a special This template parameter. As long as subclass inherits it, the subclass will automatically replace This as itself Type.
In fact, it once again shows the benefits of inheritance.
Struct should be able to inherit, or even multi inheritation like C++.

March 13, 2022
On Saturday, 12 March 2022 at 13:00:24 UTC, Timon Gehr wrote:
> The way I work around this is by putting template mixins into all subclasses, using `typeof(this)`. (It's a bit finicky though, template mixins are notorious for exposing forward reference compiler bugs.)

Yeah, I ended up doing this since I made the opening post. Luckily I've yet to experience any compiler bugs, so yourself and others must have ironed most of them out by now, lol. But to have what Adam proposed would still be amazing.

On Saturday, 12 March 2022 at 15:06:58 UTC, zjh wrote:
> Each class should have a `This` type or information like rust's `self/Self` to indicate `Self Type`.
> `The base class` should have a special `This` template parameter. As long as subclass inherits it, `the subclass` will automatically replace `This` as itself Type.
> In fact, it once again shows the `benefits` of `inheritance`.
> `Struct` should be able to inherit, or even multi inheritation like `C++`.

Mostly in agreement, but I don't really see the need for structs to have vtables and whatnot if you are given the ability to easily allocate class instances however you please.
March 13, 2022
On 3/12/2022 5:00 AM, Timon Gehr wrote:
> (It's a bit finicky though, template mixins are notorious for exposing forward reference compiler bugs.)

The fwd ref problems I see are usually caused by circular references.

March 14, 2022
On Saturday, 12 March 2022 at 05:10:10 UTC, surlymoor wrote:
> On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:
>> ...

While not exactly the same, this can be worked around by using the constructor of the base class.

```d
class Base {
    private string _stringof;
    this(this This)() { _stringof = This.stringof; }

    string serialize() {
        return _stringof;
    }
 }

 class Derived : Base {

 }

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

March 14, 2022

On Monday, 14 March 2022 at 07:48:49 UTC, bauss wrote:

>

On Saturday, 12 March 2022 at 05:10:10 UTC, surlymoor wrote:

>

On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:

>

...

but this fails:

class Base {
    string serialize(this This)() {
        string _stringof = This.stringof;
        return _stringof;
    }
 }
 class Derived : Base {
 }

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

On Monday, 14 March 2022 at 07:59:00 UTC, zjh wrote:

>

On Monday, 14 March 2022 at 07:48:49 UTC, bauss wrote:

>

On Saturday, 12 March 2022 at 05:10:10 UTC, surlymoor wrote:

>

On Thursday, 23 July 2020 at 02:38:27 UTC, Adam D. Ruppe wrote:

>

...

but this fails:

class Base {
    string serialize(this This)() {
        string _stringof = This.stringof;
        return _stringof;
    }
 }
 class Derived : Base {
 }

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

Yes, that's the point of my message, that it's not the same but it can be used to workaround, so you basically "cache" the values to use in the constructor, since you can't retrieve them elsewhere.

The code I shared will achieve the same in theory, while of course not being 1:1

March 14, 2022
On 14.03.22 07:31, Walter Bright wrote:
> On 3/12/2022 5:00 AM, Timon Gehr wrote:
>> (It's a bit finicky though, template mixins are notorious for exposing forward reference compiler bugs.)
> 
> The fwd ref problems I see are usually caused by circular references.
> 

Yes, those are usually a component, but it's much less likely to be an issue without introspection and code generation.