digitalmars.D.learn - How to require operator overloading in interface
- JS (59/59) Jul 19 2013 I would like to require any implementation of an interface to
- Adam D. Ruppe (20/20) Jul 19 2013 You can't have templates in interfaces unless they are final,
- H. S. Teoh (10/29) Jul 19 2013 [...]
- Jesse Phillips (5/5) Jul 19 2013 The relevant blog post:
- JS (8/13) Jul 20 2013 And this is a compiler limitation? After all, templated functions
- H. S. Teoh (31/48) Jul 20 2013 Nope. Templated functions don't exist until they're instantiated. Which
- JS (32/94) Jul 20 2013 Thanks. I still think that interfaces define contracts that must
- Jesse Phillips (18/25) Jul 20 2013 I understand there is a little boilerplate when wanting to have
- JS (9/34) Jul 20 2013 well, I see your point but it is less verbose AND if they want to
- Jesse Phillips (4/7) Jul 20 2013 Nope, that is why you make the template final and forwards, all
- JS (5/12) Jul 21 2013 Sorry, I had a misconception about final(I didn't realize you
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
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
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
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
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
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: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.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...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
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: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).On Saturday, 20 July 2013 at 01:37:13 UTC, Jesse Phillips wrote: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.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...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
Jul 20 2013
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
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: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").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) { ... } }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
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
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: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. Thanksif 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 21 2013