July 24, 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):
>
> 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 {
> }
>

class GrandChild : Derived {}

Please file a bug/enhancement report that 'this(this This)()' in Base doesn't work intuitively for GrandChild.

It would give a lot of expressive power with a very small fix/change... small enough that it wouldn't require a DIP, at least not in my opinion.

July 28, 2020
On Thursday, 23 July 2020 at 02:38:27 UTC, 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.


That will fail for at least one use case:
Consider class A that has templated virtual method do. It lives in library X. Then we have class B : A that is in library Y. Now application U has variable of type A that contains type B. At some point U calls 'do' with an int. How should compiler proceed with such situation given Y is available only in compiled version?

Virtual template methods could work if D would have generics similar to Java, which are basically syntactic sugar for dynamic casts.

Best regards,
Alexandru.
July 28, 2020
On 7/28/20 5:01 PM, Alexandru Ermicioi wrote:
> On Thursday, 23 July 2020 at 02:38:27 UTC, 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.
> 
> 
> That will fail for at least one use case:
> Consider class A that has templated virtual method do. It lives in library X. Then we have class B : A that is in library Y. Now application U has variable of type A that contains type B. At some point U calls 'do' with an int. How should compiler proceed with such situation given Y is available only in compiled version?

It should work. The idea is that the derived type generates the template code and stores it in the vtable. It's not much different from the solution Max Samukha posted, except the compiler should do it instead of the user, and it's put in the vtable instead of the instance.

So it doesn't matter where the code is stored, it is already concrete and part of the vtable.

-Steve
July 29, 2020
On Tuesday, 28 July 2020 at 21:09:51 UTC, Steven Schveighoffer wrote:
>
> So it doesn't matter where the code is stored, it is already concrete and part of the vtable.
>
> -Steve

Well it does. How do you plan to handle the case where Y library is already compiled (separately, or available as .lib with .di interface)? Vtable should already be defined, which means that upon loading of lib, main app needs to patch vtable with missing template specializations.

Best regards,
Alexandru.
July 29, 2020
On 29/7/20 10:01, Alexandru Ermicioi wrote:
> Well it does. How do you plan to handle the case where Y library is already compiled (separately, or available as .lib with .di interface)? Vtable should already be defined, which means that upon loading of lib, main app needs to patch vtable with missing template specializations.
> 
> Best regards,
> Alexandru.

I think the trick here is that they wouldn't actually be templates in the usual sense. So:

```
class Base {
    virtual void fun(this This)() { /* body */ }
}
```

would be lowered to:

```
class Base {
    void fun() {
        alias This = Base;
        /* body */
    }
}
```

The code for `fun` would still be available in the .di file (as with any other template), so when you then later do:

```
class Derived : Base { }
```

it would be rewritten / lowered as:

```
class Derived : Base {
    override void fun() {
        alias This = Derived;
        /* body */
    }
}
```

that could be then perfectly interact with all other compilation units.

I'm happy to be corrected if I understood it wrong, but I think it could work this way without any side-effect (i.e. you can try the lowered version now and it works):

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

class Derived : Base {
    override string serialize() {
        alias This = Derived;
	return This.stringof; // just for example
    }
}

void main() {
    Base b = new Derived();
    assert(b.serialize() == "Derived");
}
```
July 29, 2020
On 7/29/20 4:01 AM, Alexandru Ermicioi wrote:
> On Tuesday, 28 July 2020 at 21:09:51 UTC, Steven Schveighoffer wrote:
>>
>> So it doesn't matter where the code is stored, it is already concrete and part of the vtable.
>>
> 
> Well it does. How do you plan to handle the case where Y library is already compiled (separately, or available as .lib with .di interface)? Vtable should already be defined, which means that upon loading of lib, main app needs to patch vtable with missing template specializations.
> 

This statement suggests you are not understanding the proposal. Arafel is right -- it's not a template function to be instantiated on calling, but a concrete function in the vtable instantiated on declaration.

I can't explain it better than Arafel did.

-Steve
July 29, 2020
On Wednesday, 29 July 2020 at 12:17:55 UTC, Steven Schveighoffer wrote:
> On 7/29/20 4:01 AM, Alexandru Ermicioi wrote:>> On Tuesday, 28 July 2020 at 21:09:51 UTC, Steven Schveighoffer This statement suggests you are not understanding the proposal. Arafel is right -- it's not a template function to be instantiated on calling, but a concrete function in the vtable instantiated on declaration.

Alright, I did read again original post, and indeed it is about (this T) case only, which may work. I was referring to broader case of templates in general, which would have issues I mentioned. Still, for these virtual (this T) methods, it's best to have a different/modified syntax to existing x(this T)(), since they mean two different things.

And this feature can be implemented also in following fashion right now:

---
class Base {
  protected string doImpl(this T)() {
    return T.stringof;
  }

  public abstract do();
}

class Derived {
  public do() {
    this.doImpl();
  }
}
---

imho, this feature can be lowered to code in example above, instead of generating new body for each derived class.

Best regards,
Alexandru.
July 29, 2020
On 29/7/20 15:04, Alexandru Ermicioi wrote:
> imho, this feature can be lowered to code in example above, instead of generating new body for each derived class.

Sure, I was just making an example to show how it would work. I leave the inner details on how it would be implemented to more knowledgeable people than me :-)

Still, in your version, at the end of the day you're instantiating anyway the template at each class, so the code will also be generated, and even with an indirection (ok, it should be optimized / inlined). Also it could be tricky with things such as attributes, etc.

One possible issue I see is what would happen with template-specific features, like `auto ref`, that wouldn't be available in this case, for instance:

```
class Base {
    virtual foo(this This)(auto ref bar) { }
}
```

or if you use This as the type of a parameter:

```
class Base {
    virtual foo(this This)(This bar) { }
}

class Derived : Base { }

void main() {
    Base b = new Derived;
    b.foo(new Base); // Fails to compile?
}
```

You could of course special-case the syntax to disable these cases, but for the users it would be an unexpected outlier in how the templates work.
July 29, 2020
On 7/29/20 9:37 AM, Arafel wrote:

> You could of course special-case the syntax to disable these cases, but for the users it would be an unexpected outlier in how the templates work.

I think it's a 2-stage process. 1) instantiate the template with the type ONLY. If it can't be instantiated, then it's not valid. 2) check for normal overriding rules. If it cannot override the base function, then it's an error.

One difference might have to be attribute inference, as you may not want the template to infer more restrictive attributes than you need, and there is no way to say e.g. @gc.

-Steve
1 2 3
Next ›   Last »