Jump to page: 1 2 3
Thread overview
Dynamic templated virtuals - I still often want them
Jul 23, 2020
Adam D. Ruppe
Jul 23, 2020
Manu
Jul 23, 2020
Adam D. Ruppe
Jul 23, 2020
Max Samukha
Jul 23, 2020
Arafel
Jul 23, 2020
Adam D. Ruppe
Jul 24, 2020
Tove
Jul 23, 2020
Arafel
Jul 23, 2020
Arafel
Jul 23, 2020
Tove
Jul 23, 2020
Arafel
Jul 23, 2020
Arafel
Jul 23, 2020
Tove
Jul 23, 2020
Tove
Jul 23, 2020
Adam D. Ruppe
Jul 23, 2020
Adam D. Ruppe
Jul 23, 2020
Tove
Jul 23, 2020
Adam D. Ruppe
Jul 23, 2020
Adam D. Ruppe
Jul 28, 2020
Alexandru Ermicioi
Jul 29, 2020
Alexandru Ermicioi
Jul 29, 2020
Arafel
Jul 29, 2020
Alexandru Ermicioi
Jul 29, 2020
Arafel
July 23, 2020
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.
July 23, 2020
On Thu, Jul 23, 2020 at 12:40 PM Adam D. Ruppe via Digitalmars-d < digitalmars-d@puremagic.com> 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.
>

That's clever, and I wish I had have thought of it before!!
This pattern would have helped me in multiple scenarios I've encountered in
the past where I had to deploy awkward and ugly mixin tricks.


July 23, 2020
On 23/7/20 4:38, Adam D. Ruppe wrote:
> 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`.

This does work today, you just don't need the `virtual` keyword:

```
class Base {
     string serialize(this This)() {
           return This.stringof; // just for example
     }
}

class Derived : Base {

}

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

https://run.dlang.io/is/vp1koK

I have used this to do some nice tricks. What is missing is to make the `this` parameter work out of normal function, so with static functions, and even out of functions:

https://issues.dlang.org/show_bug.cgi?id=10488
https://issues.dlang.org/show_bug.cgi?id=20277

Specifically, I want to be able to transform Compile-time introspection into run-time instrospection:

```

TypeInfo_Class[string] rtIntrospection;

class Base {
	shared static this (this This) {
		rtIntrospection[This.stringof] = This.classinfo;
	}
}

class Derived : Base { } // The entry is added automatically
```

Now if you want to have something like this you have to depend on the end user registering each class manually.
July 23, 2020
On 23/7/20 10:46, Arafel wrote:
> 
> ```
> 
> TypeInfo_Class[string] rtIntrospection;
> 
> class Base {
>      shared static this (this This) {
>          rtIntrospection[This.stringof] = This.classinfo;
>      }
> }
> 
> class Derived : Base { } // The entry is added automatically
> ```

Sorry, there's a missing set of parentheses there, it should of course be:


```

TypeInfo_Class[string] rtIntrospection;

class Base {
    shared static this (this This)() {
        rtIntrospection[This.stringof] = This.classinfo;
    }
}

class Derived : Base { } // The entry is added automatically
```
July 23, 2020
On Thursday, 23 July 2020 at 08:46:54 UTC, Arafel wrote:
> On 23/7/20 4:38, Adam D. Ruppe wrote:
>> 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`.
>
> This does work today, you just don't need the `virtual` keyword:
>
> ```
> class Base {
>      string serialize(this This)() {
>            return This.stringof; // just for example
>      }
> }
>
> class Derived : Base {
>
> }
>
> void main() {
>     Base b = new Derived();
>     assert(b.serialize() == "Derived");
> }
> ```
>
> https://run.dlang.io/is/vp1koK
>

It compiles but doesn't work, in your link you compiled with "-c", so the runtime assert was never evalutated.



July 23, 2020
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.
>
>
> 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.

Yes, please! I recall similar ideas resurfacing multiple times over the course of at least 7 years from different people. Back then we, didn't have a good DIP process so noone could take it to the next level.

I am convinced this is a killer enabler feature. As it is today, many people that has been using D for a long time have their own more or less ugly mixin workarounds, but it's not easy for everyone just starting with D to come up with this pattern themselves.

July 23, 2020
On 23/7/20 11:51, Tove wrote:
> It compiles but doesn't work, in your link you compiled with "-c", so the runtime assert was never evalutated.

Ups! Sorry, I feel quite dumb now... I had the `-c` left from some previous tests and didn't notice.

I have indeed reviewed my code, and the caller is also templated on the actual class, i.e., it's also calling the method with the derived type.

And in fact, now that I think about it, it can't possibly work at compile time. Consider:

```
Base b = uniform(0,2) ? new Base() : new Derived();
```

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:

```
module foo;

import std;

class Base {
     string serialize() {
         return typeid(this).toString; // just for example
     }
}

class Derived : Base {

}

void main() {
	Base b = new Derived();
    assert(b.serialize == "foo.Derived");
}
```
July 23, 2020
On 23/7/20 12:18, Arafel wrote:
> 
> And in fact, now that I think about it, it can't possibly work at compile time. Consider:
> 
> ```
> Base b = uniform(0,2) ? new Base() : new Derived();
> ```

Forget it, I misunderstood the proposal, I'm feeling dumber and dumber now :-/

So the function would be instantiated again for each implementing class.

It would be really cool and I like it, but I've got the feeling that it's just working around the fact that there is no proper RT introspection yet.

If you could do RT introspection the same way you can do it at compile time (for instance access to the individual fields), this wouldn't be needed.
July 23, 2020
On Thursday, 23 July 2020 at 10:18:34 UTC, Arafel wrote:
> On 23/7/20 11:51, Tove wrote:
>> It compiles but doesn't work, in your link you compiled with "-c", so the runtime assert was never evalutated.
>
> Ups! Sorry, I feel quite dumb now... I had the `-c` left from some previous tests and didn't notice.
>
> I have indeed reviewed my code, and the caller is also templated on the actual class, i.e., it's also calling the method with the derived type.
>
> And in fact, now that I think about it, it can't possibly work at compile time. Consider:
>
> ```
> Base b = uniform(0,2) ? new Base() : new Derived();
> ```
>
> 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:
>
> ```
> module foo;
>
> import std;
>
> class Base {
>      string serialize() {
>          return typeid(this).toString; // just for example
>      }
> }
>
> class Derived : Base {
>
> }
>
> void main() {
> 	Base b = new Derived();
>     assert(b.serialize == "foo.Derived");
> }
> ```

Yes, this works fine. It has at least two issues though.

1) Slower than a virtual function call, but I suppose the result of typeid could be cached to mitigate this issue.

2) TypeInfo cannot be used with -betterC

July 23, 2020
On Thursday, 23 July 2020 at 10:27:14 UTC, Arafel wrote:
> On 23/7/20 12:18, Arafel wrote:
>
> If you could do RT introspection the same way you can do it at compile time (for instance access to the individual fields), this wouldn't be needed.

I had one project where I had to solve the same problem both in C# and in D.

In C# I first used 'dynamic' for prototyping.
+ Simple
- Slow
Later I had to optimize it by doing "RT introspection" and caching the result.
+ Fast
- Somewhat complex

Adams proposal is both simple and fast out of the box.

« First   ‹ Prev
1 2 3