Thread overview
Defining and overriding methods of an abstract base class which must accept a template parameter
Jul 10, 2016
pineapple
Jul 10, 2016
Basile B.
Jul 10, 2016
Basile B.
Jul 10, 2016
pineapple
Jul 10, 2016
Basile B.
Jul 10, 2016
Jonathan M Davis
July 10, 2016
This is essentially what I'm trying to accomplish. The intuitive solution, of course, does not work. In theory I could write a separate method for every anticipated return type, but that would be horrible and in that case I'd probably just write the damn thing in a dynamically-typed language instead.

    import std.conv;

    abstract class BaseClass{
        abstract X convertSomePropertyTo(X)();
    }

    class SubClass(T): BaseClass{
        T property;
        X convertSomePropertyTo(X)(){
            return property.to!X;
        }
    }

    void main(){
        BaseClass obj = new SubClass!int;
        auto x = obj.convertSomePropertyTo!real; // Error: function test.BaseClass.convertSomePropertyTo!real.convertSomePropertyTo non-virtual functions cannot be abstract
    }

July 10, 2016
On Sunday, 10 July 2016 at 21:06:42 UTC, pineapple wrote:
> This is essentially what I'm trying to accomplish. The intuitive solution, of course, does not work. In theory I could write a separate method for every anticipated return type, but that would be horrible and in that case I'd probably just write the damn thing in a dynamically-typed language instead.
>
>     import std.conv;
>
>     abstract class BaseClass{
>         abstract X convertSomePropertyTo(X)();
>     }
>
>     class SubClass(T): BaseClass{
>         T property;
>         X convertSomePropertyTo(X)(){
>             return property.to!X;
>         }
>     }
>
>     void main(){
>         BaseClass obj = new SubClass!int;
>         auto x = obj.convertSomePropertyTo!real; // Error: function test.BaseClass.convertSomePropertyTo!real.convertSomePropertyTo non-virtual functions cannot be abstract
>     }

The problem you encounter here is that templatized functions cannot be virtual. If you remove "abstract" and put an empty body than it works, but you lose the whole OOP thing, i.e you cannot call the most derived override from the base.

====
import std.conv;

abstract class BaseClass
{
    auto convertSomePropertyTo(X)(){};
}

class SubClass(T): BaseClass
{
    T property;
    X convertSomePropertyTo(X)()
    {
        return property.to!X;
    }
}

void main()
{
    SubClass!int obj = new SubClass!int;
    auto x = obj.convertSomePropertyTo!real;
}
===

We are many to encounter this issue. It comes from the fact that the VTBL cannot be build from a template.
July 10, 2016
On Sunday, 10 July 2016 at 21:20:34 UTC, Basile B. wrote:
> On Sunday, 10 July 2016 at 21:06:42 UTC, pineapple wrote:
>> [...]
> It comes from the fact that the VTBL cannot be build from a template.

See https://issues.dlang.org/show_bug.cgi?id=1657#c1


July 10, 2016
On Sunday, 10 July 2016 at 21:20:34 UTC, Basile B. wrote:
> The problem you encounter here is that templatized functions cannot be virtual. If you remove "abstract" and put an empty body than it works, but you lose the whole OOP thing, i.e you cannot call the most derived override from the base.

Yeah, that was the first thing I tried, and it took me a while and some measure of annoyance to realize the base method was being called instead of the derived one.

Surely there's some reasonable workaround?
July 10, 2016
On Sunday, 10 July 2016 at 21:27:14 UTC, pineapple wrote:
> On Sunday, 10 July 2016 at 21:20:34 UTC, Basile B. wrote:
>> The problem you encounter here is that templatized functions cannot be virtual. If you remove "abstract" and put an empty body than it works, but you lose the whole OOP thing, i.e you cannot call the most derived override from the base.
>
> Yeah, that was the first thing I tried, and it took me a while and some measure of annoyance to realize the base method was being called instead of the derived one.
>
> Surely there's some reasonable workaround?

AFAIK no. OOP and templates don't mix well as main paradigm. This doesn't mean that templates can't be used in OOP (eg as template param for a class of for some helpers used in virtual function) but you see that one can break the other.
July 10, 2016
On Sunday, July 10, 2016 21:27:14 pineapple via Digitalmars-d-learn wrote:
> On Sunday, 10 July 2016 at 21:20:34 UTC, Basile B. wrote:
> > The problem you encounter here is that templatized functions cannot be virtual. If you remove "abstract" and put an empty body than it works, but you lose the whole OOP thing, i.e you cannot call the most derived override from the base.
>
> Yeah, that was the first thing I tried, and it took me a while and some measure of annoyance to realize the base method was being called instead of the derived one.
>
> Surely there's some reasonable workaround?

You can have an abstract non-templated function which then calls a templated function in the derived class' implementation, and you can have a templated function in the base class which then calls an abstract, non-templated function that the derived class implements. But if you actually need the templated function to be virtual, then there is no way to do it.

For instance, overloaded operators usually can't be virtual in D, because most of them are templated. However, a workaround for them is to declare the overloaded operator on the base class and then have it call a non-templated function which the derived classes override. e.g. a typical opBinary would look something like

class C
{
    C opBinary(string op)(C rhs)
        if(["+, "-", "*", "-"].canFind(op))
    {
        mixin("return this.value " ~ op ~ " rhs.value;");
    }

    int value;
}

but that can't be virtual. So, you if you need virtual, overloaded operators, then you do something like

class C
{
    C opBinary(string op)(C rhs)
        if(op == "=")
    {
        return doAdd(rhs);
    }

    C opBinary(string op)(C rhs)
        if(op == "-")
    {
        return doSub(rhs);
    }

    C opBinary(string op)(C rhs)
        if(op == "*")
    {
        return doMul(rhs);
    }

    C opBinary(string op)(C rhs)
        if(op == "/")
    {
        return doDiv(rhs);
    }

    protected abstract C doAdd(C c);
    protected abstract C doSub(C c);
    protected abstract C doMul(C c);
    protected abstract C doDiv(C c);

    int value;
}

But that only works, because you're not really dealing with an arbitrary list of possible, template arguments. If you really need it to work with a list of unknown, possible, template arguments, then you're out of luck.

The only way that you're going to get what you're trying to do to work is if you can find a way to define a non-templated function (or set of functions) that can be virtual and do the work that you need to be virtual.

- Jonathan M Davis