www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to require operator overloading in interface

reply "JS" <js.mdnq gmail.com> writes:
I would like to require any implementation of an interface to 
override

import std.stdio, std.cstream;

interface A
{
	void opOpAssign(string op : "^")(int c);
}

class B : A
{
	int x;
	void opOpAssign(string op : "+")(int c) { x = c; }
         // Note it uses +
}

void main(string[] argv)
{
	
	B b = new B;

	b ^= 3;
	writeln(b.x);
	din.getc();
}

but the compiler gives an error that is 3 and b are incompatible 
types.. I'm not sure if ^= is overloaded in some default way or 
what but there is no error about b not implementing the property 
OpAssign.

If I use override for opOpassign in B, dmd says it can't override 
a non-virtual function. This is a bit strange as it would seem it 
would break inheritance.


The following code produces an undefined symbol... I could make 
A.opOpAssign final but then inheritance because a casualty.

import std.stdio, std.cstream;

interface A
{
	void opOpAssign(string op)(int c) if (op == "^");
}

class B : A
{
	int x;
	void opOpAssign(string op : "^")(int c) { x = c; }
}

class C : B
{
	void opOpAssign(string op : "^")(int c) { x = c+2; }
}

void main(string[] argv)
{
	
	B b = new B;
	C c = new C;
	A a = new C;

	b ^= 3;
	writeln(b.x);
	c ^= 3;
	writeln(c.x);
	a ^= 3;

	writeln((cast(C)a).x);
	din.getc();
}


Let me guess... this is a feature of D?
Jul 19 2013
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
You can't have templates in interfaces unless they are final, 
otherwise it won't work right.

The way I'd do it is is make the op template final, and have it 
forward to another normal virtual function:

interface Addable {
    final Addable opBinary(string op : "+")(Addable rhs) {
         return this.opAdd(rhs);
    }

    /* virtual */ Addable opAdd(Addable rhs);
}

class Test : Addable {
     override Addable opAdd(Addable rhs) {
             return new Test();
     }
}

void main() {
    auto test = new Test();
    auto test2 = new Test();

    auto sum = test + test2;
}
Jul 19 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jul 19, 2013 at 04:28:42PM +0200, Adam D. Ruppe wrote:
 You can't have templates in interfaces unless they are final,
 otherwise it won't work right.
 
 The way I'd do it is is make the op template final, and have it
 forward to another normal virtual function:
 
 interface Addable {
    final Addable opBinary(string op : "+")(Addable rhs) {
         return this.opAdd(rhs);
    }
 
    /* virtual */ Addable opAdd(Addable rhs);
 }
 
 class Test : Addable {
     override Addable opAdd(Addable rhs) {
             return new Test();
     }
 }
[...] I'd add that you need to do this for base classes too, if you want overloaded operators to be overridable by derived classes. Template methods and inheritance don't mix together very well. T -- The diminished 7th chord is the most flexible and fear-instilling chord. Use it often, use it unsparingly, to subdue your listeners into submission!
Jul 19 2013
prev sibling parent reply "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
The relevant blog post:

http://3d.benjamin-thaut.de/?p=94

What you should understand is template functions are not/can not 
be virtual. They do not exist until they are instantiated. Thus 
you can not require that they be overloaded.
Jul 19 2013
parent reply "JS" <js.mdnq gmail.com> writes:
On Saturday, 20 July 2013 at 01:37:13 UTC, Jesse Phillips wrote:
 The relevant blog post:

 http://3d.benjamin-thaut.de/?p=94

 What you should understand is template functions are not/can 
 not be virtual. They do not exist until they are instantiated. 
 Thus you can not require that they be overloaded.
And this is a compiler limitation? After all, templated functions are ultimately just normal functions... In my case the template parameter is explicitly known and the overloadable operations being templated isn't my fault. I guess I can redirect each templated function to a non-templated version using the method in benjamin's page but this seems like it defeats exactly what templates are for...
Jul 20 2013
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Jul 20, 2013 at 12:13:49PM +0200, JS wrote:
 On Saturday, 20 July 2013 at 01:37:13 UTC, Jesse Phillips wrote:
The relevant blog post:

http://3d.benjamin-thaut.de/?p=94

What you should understand is template functions are not/can not
be virtual. They do not exist until they are instantiated. Thus
you can not require that they be overloaded.
And this is a compiler limitation? After all, templated functions are ultimately just normal functions...
Nope. Templated functions don't exist until they're instantiated. Which means the compiler can't know in advance how many instantiations there will be and which ones they are. And given that D supports separate compilation, you *can't* know this (even after linking, there's the possibility that some external dynamic library might instantiate yet another version of the function). There are ways of implementing overloadable templated functions, of course. But they are rather complicated to implement, and incur runtime overhead.
 In my case the template parameter is explicitly known and the
 overloadable operations being templated isn't my fault.
 
 I guess I can redirect each templated function to a non-templated
 version using the method in benjamin's page but this seems like it
 defeats exactly what templates are for...
Not really. Having operator overloading implemented as template functions give you more flexibility (albeit at the cost of more code complexity). You could either redirect each operator to an overridable function, or you could do this: class MyClass { auto opBinary(string op)(T t) { // Buahaha, now op is a runtime parameter return opBinaryImpl(op, t); } auto opBinaryImpl(string op, T t) { // which means this method is overridable in // derived classes. ... } } You could also do this only for a certain subset of operators, depending on what granularity you wish to have. I'd write more, but I gotta run. Maybe later. T -- "Hi." "'Lo."
Jul 20 2013
parent reply "JS" <js.mdnq gmail.com> writes:
On Saturday, 20 July 2013 at 13:27:03 UTC, H. S. Teoh wrote:
 On Sat, Jul 20, 2013 at 12:13:49PM +0200, JS wrote:
 On Saturday, 20 July 2013 at 01:37:13 UTC, Jesse Phillips 
 wrote:
The relevant blog post:

http://3d.benjamin-thaut.de/?p=94

What you should understand is template functions are not/can 
not
be virtual. They do not exist until they are instantiated. 
Thus
you can not require that they be overloaded.
And this is a compiler limitation? After all, templated functions are ultimately just normal functions...
Nope. Templated functions don't exist until they're instantiated. Which means the compiler can't know in advance how many instantiations there will be and which ones they are. And given that D supports separate compilation, you *can't* know this (even after linking, there's the possibility that some external dynamic library might instantiate yet another version of the function). There are ways of implementing overloadable templated functions, of course. But they are rather complicated to implement, and incur runtime overhead.
 In my case the template parameter is explicitly known and the
 overloadable operations being templated isn't my fault.
 
 I guess I can redirect each templated function to a 
 non-templated
 version using the method in benjamin's page but this seems 
 like it
 defeats exactly what templates are for...
Not really. Having operator overloading implemented as template functions give you more flexibility (albeit at the cost of more code complexity). You could either redirect each operator to an overridable function, or you could do this: class MyClass { auto opBinary(string op)(T t) { // Buahaha, now op is a runtime parameter return opBinaryImpl(op, t); } auto opBinaryImpl(string op, T t) { // which means this method is overridable in // derived classes. ... } } You could also do this only for a certain subset of operators, depending on what granularity you wish to have. I'd write more, but I gotta run. Maybe later. T
Thanks. I still think that interfaces define contracts that must be followed. If define an interfaces to use an operator I think any inheritance of it must implement that operator. This might not work for templated members but should work for them if they are specialized. e.g., we might not be able to do opOpAssign(string)() but we should be able to use opOpAssign(string op : "+")() to enforce a contract... in this case it not really any different than the code you show excep it is more natural(instead of opBinaryImpl we actually get to use opOpAssign as the name). e.g., class MyClass { auto opBinary(string op : "|" T : int)(T t) { } // opBinary is completely specialized and is no different than a regular function, it can be overridden directly in children without having to use a redirection. (note in your opBinaryImpl, T must be specified } This way any children of MyClass *can* override the operation if necessary and inheritance actually works for templates to some degree. In fact, it should almost alway work because type parameter information can be used to pass to a normal function. auto opBinary(string op, T)(T t) { returnopBinaryImpl(op, typeinfo(T), cast(object)t); } auto opBinaryImpl(string op, typeinfo type, object value) { } so we could use this for the general case but then have the specifications for the cases we know about at the time of design.... but users of the base class can design their own operators to use(at a performance cost).
Jul 20 2013
parent reply "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Saturday, 20 July 2013 at 16:46:52 UTC, JS wrote:
  	class MyClass {
  		auto opBinary(string op : "|" T : int)(T t) { }
                 // opBinary is completely specialized and is no 
 different than a regular function, it can be overridden 
 directly in children without having to use a redirection. (note 
 in your opBinaryImpl, T must be specified
  	}
I understand there is a little boilerplate when wanting to have overriding for the operator overloads, but I don't see much gain from the proposal. Do you really want to force people to write int opBinary(string op : "|" T : int)(T t) instead of override int opOr(int t) when trying to override your function? Though it might be interesting if an aliased function could be overridden: class I { final int opBinary(string op)(int t) { ... } alias opAdd = opBinary!"+"; } class MyClass : I{ override int opAdd(int v) { ... } }
Jul 20 2013
parent reply "JS" <js.mdnq gmail.com> writes:
On Saturday, 20 July 2013 at 18:34:30 UTC, Jesse Phillips wrote:
 On Saturday, 20 July 2013 at 16:46:52 UTC, JS wrote:
 	class MyClass {
 		auto opBinary(string op : "|" T : int)(T t) { }
                // opBinary is completely specialized and is no 
 different than a regular function, it can be overridden 
 directly in children without having to use a redirection. 
 (note in your opBinaryImpl, T must be specified
 	}
I understand there is a little boilerplate when wanting to have overriding for the operator overloads, but I don't see much gain from the proposal. Do you really want to force people to write int opBinary(string op : "|" T : int)(T t) instead of override int opOr(int t) when trying to override your function?
well, I see your point but it is less verbose AND if they want to allow the same op in there class, which they probably do, they have to override the long hand anyways and redirect. So all in all I think the long hand is better(more consistent with "previous practices").
 Though it might be interesting if an aliased function could be 
 overridden:

 class I {
     final int opBinary(string op)(int t) { ... }
     alias opAdd = opBinary!"+";
 }

 class MyClass : I{
     override int opAdd(int v) { ... }
 }
I think this would be better. This way either choice could be used but there is no extra redirection that takes place(well, through the alias but that is symbolic).
Jul 20 2013
parent reply "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Saturday, 20 July 2013 at 18:53:44 UTC, JS wrote:
 if they want to allow the same op in there class, which they 
 probably do, they have to override the long hand anyways and 
 redirect.
Nope, that is why you make the template final and forwards, all derived classes will be able to instantiate the template and behavior will be overridden by the forwarded function.
Jul 20 2013
parent "JS" <js.mdnq gmail.com> writes:
On Saturday, 20 July 2013 at 19:04:29 UTC, Jesse Phillips wrote:
 On Saturday, 20 July 2013 at 18:53:44 UTC, JS wrote:
 if they want to allow the same op in there class, which they 
 probably do, they have to override the long hand anyways and 
 redirect.
Nope, that is why you make the template final and forwards, all derived classes will be able to instantiate the template and behavior will be overridden by the forwarded function.
Sorry, I had a misconception about final(I didn't realize you could call a final method of a base class in a derived class). I see it will work as described. Thanks
Jul 21 2013