www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Templated operator overloading

reply XavierAP <n3minis-git yahoo.es> writes:
I've been trying some things to template operator overloads. The 
reason is that I want very similar code for different types, but 
I can't use polymorphism, as they're structs rather than classes. 
Perhaps this choice is not as advantageous as I think, and I may 
change this design from structs to classes, or else the code 
duplication would be small and never subject to change. But now 
I'm just trying for the sake of learning to find out what works 
or not in terms of templated operator overloading, and whether 
the reason something doesn't work is by design and if mentioned 
in the specification, or just an arbitraty result of some 
unspecified parsing/lowering step order, or it depends on the 
compiler (I'm using dmd).

Since there are in my case two similar types (below just a 
minimal dumb proof of concept), I want the operator(s) to work 
within the same type, or also with the other. The following code 
actually works, including type parameter inferrence, and const 
ref to avoid struct copying:

/********************/
import std.stdio;

struct S1
{
	void opOpAssign(string op, T)(const ref T x)
	{
		writeln(this, op, x);
	}
}
struct S2
{}

void main()
{	
	S1 s1;
	S2 s2;
	s1 *= s2;
}
/********************/

When I want to have the same operator overloading code in both 
types however, I can't make it work:

/********************/
private mixin template operator(Tthis)
{
	void opOpAssign(string op, T)(ref Tthis, const ref T x)
	{
		writeln(this, op, x);
	}
}

struct S1
{
	mixin operator!S1;
}
struct S2
{
	mixin operator!S2;
}

void main()
{	
	S1 s1;
	S2 s2;
	s1 *= s2; // Error: s1 *= s2 is not a scalar
	s1.opOpAssign!"*"(s2); // Error: template 
test.S1.operator!(S1).opOpAssign cannot deduce function
}
/********************/

And a final try with a global templated function instead of a 
mixin template:

/********************/
private void opOpAssign(string op, Tthis, T)(ref Tthis that, 
const ref T x)
{
	writeln(that, op, x);
}

struct S1
{}
struct S2
{}

void main()
{	
	S1 s1;
	S2 s2;
	s1 *= s2; // Error: s1 *= s2 is not a scalar
	s1.opOpAssign!"*"(s2); // OK!
}
/********************/
Aug 22 2018
next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 22 August 2018 at 11:58:25 UTC, XavierAP wrote:
 When I want to have the same operator overloading code in both 
 types however, I can't make it work:
From https://dlang.org/spec/operatoroverloading.html#binary: "the one with the ‘better’ match is selected. It is an error for both to equally match." Since both your opOpAssigns match equally, the compiler throws up. The solution is to add some sort of restriction: struct S1 { mixin opOver; } struct S2 { mixin opOver; } mixin template opOver() { auto opOpAssign(string op, T)(T rhs) if (T.stringof > typeof(this).stringof) { // Here import std.stdio; writeln(this, op, rhs); } } unittest { S1 a; S2 b; a += b; }
 And a final try with a global templated function instead of a 
 mixin template:

 /********************/
 private void opOpAssign(string op, Tthis, T)(ref Tthis that, 
 const ref T x)
This syntax would either enable the definition of operators on builtin types that shouldn't have them, or be perceived as inconsistent, so invasive operator overloading is used in D instead. -- Simen
Aug 22 2018
parent XavierAP <n3minis-git yahoo.es> writes:
On Wednesday, 22 August 2018 at 12:36:39 UTC, Simen Kjærås wrote:
 Since both your opOpAssigns match equally, the compiler throws 
 up. The solution is to add some sort of restriction:
This doesn't happen apparently: the operator has a left and a right side, even if both types define the operator, only one of them is on the left at each call. It works now after Ali corrected my stupid syntax :)
Aug 22 2018
prev sibling parent reply aliak <something something.com> writes:
On Wednesday, 22 August 2018 at 11:58:25 UTC, XavierAP wrote:
 I've been trying some things to template operator overloads. 
 The reason is that I want very similar code for different 
 types, but I can't use polymorphism, as they're structs rather 
 than classes. Perhaps this choice is not as advantageous as I 
 think, and I may change this design from structs to classes, or 
 else the code duplication would be small and never subject to 
 change. But now I'm just trying for the sake of learning to 
 find out what works or not in terms of templated operator 
 overloading, and whether the reason something doesn't work is 
 by design and if mentioned in the specification, or just an 
 arbitraty result of some unspecified parsing/lowering step 
 order, or it depends on the compiler (I'm using dmd).

 [...]
"void opOpAssign(string op, T)(ref Tthis, const ref T x)" looks like the wrong signature for opOpAssign. THink it needs to be: void opOpAssign(string op, T)(const ref T x) Then: mixin template operator! { void opOpAssign(string op, T)(const ref T x) { writeln(this, op, x); } } struct S1 { mixin operator; } struct S2 { mixin operator; } Cheers, - Ali
Aug 22 2018
parent XavierAP <n3minis-git yahoo.es> writes:
On Wednesday, 22 August 2018 at 13:20:01 UTC, aliak wrote:
 "void opOpAssign(string op, T)(ref Tthis, const ref T x)" looks 
 like the wrong signature for opOpAssign.
Oh I'll put on my stupid hat now... I realize I had copy-pasted the wrong syntax from the global function attempt, but I swear I thought I had re-typed and tested the right one... It's working now :)
Aug 22 2018