Thread overview
Mixin Templates and Operators
Apr 06, 2022
Tejas
Apr 06, 2022
Adam D Ruppe
Apr 06, 2022
Adam D Ruppe
April 06, 2022

I have two structs, point and vec, in which I want to implement some arithmetic operations.
The operations are:

point - point = vec
point + vec = point

Using mixin templates the code compiles but calling the operations in the main causes an "incompatible type" Error:

mixin template sum(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "+"){
      return R(x + rhs.x, y + rhs.y, z + rhs.z);
   }
}

mixin template diff(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "-") {
      return R(x - rhs.x, y - rhs.y, z - rhs.z);
   }
}

struct point{
   float x, y, z;
   mixin diff!(point, vec);
   mixin sum!(vec, point);
}

struct vec{
   float x, y, z;
}

void main(){
   point p1 = {1,2,3}, p2 = {5,6,7};
   vec v1 = p1-p2;

   vec v2 = {2,-1,0};
   point p3 = p1+v2;
}

The output of dub run is

Performing "debug" build using /usr/bin/dmd for x86_64.
query ~master: building configuration "application"...
source/app.d(27,13): Error: incompatible types for `(p1) - (p2)`: both operands are of type `point`
source/app.d(30,15): Error: incompatible types for `(p1) + (v2)`: `point` and `vec`
/usr/bin/dmd failed with exit code 1.

I tried to implement a single template:

mixin template sumDiff(T, R){
   R opBinary(string op)(T rhs) const
   if (op == "+" || op == "-"){
      return mixin("R(x " ~ op ~ " rhs.x, y " ~ op ~ "rhs.y, z " ~ op ~ " rhs.z)");
   }
}

but the same error occurs.

If I don't use the mixin templates and, instead, I overload the operations into the structures everything works.

struct point{
   float x, y, z;

   vec opBinary(string op)(point rhs) const
   if (op == "-") {
      return vec(x - rhs.x, y - rhs.y, z - rhs.z);
   }

   point opBinary(string op)(vec rhs) const
   if (op == "+") {
      return point(x + rhs.x, y + rhs.y, z + rhs.z);
   }
}

struct vec{
   float x, y, z;
}

void main(){
   point p1 = {1,2,3}, p2 = {5,6,7};
   vec v1 = p1-p2;

   vec v2 = {2,-1,0};
   point p3 = p1+v2;
}

Am I doing something wrong or it is impossible to use mixin templates like that?

April 06, 2022

On Wednesday, 6 April 2022 at 10:36:04 UTC, francesco.andreetto wrote:

>

I have two structs, point and vec, in which I want to implement some arithmetic operations.
The operations are:

point - point = vec
point + vec = point

Using mixin templates the code compiles but calling the operations in the main causes an "incompatible type" Error:

mixin template sum(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "+"){
      return R(x + rhs.x, y + rhs.y, z + rhs.z);
   }
}

mixin template diff(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "-") {
      return R(x - rhs.x, y - rhs.y, z - rhs.z);
   }
}

struct point{
   float x, y, z;
   mixin diff!(point, vec);
   mixin sum!(vec, point);
}

struct vec{
   float x, y, z;
}

void main(){
   point p1 = {1,2,3}, p2 = {5,6,7};
   vec v1 = p1-p2;

   vec v2 = {2,-1,0};
   point p3 = p1+v2;
}

The output of dub run is

Performing "debug" build using /usr/bin/dmd for x86_64.
query ~master: building configuration "application"...
source/app.d(27,13): Error: incompatible types for `(p1) - (p2)`: both operands are of type `point`
source/app.d(30,15): Error: incompatible types for `(p1) + (v2)`: `point` and `vec`
/usr/bin/dmd failed with exit code 1.

I tried to implement a single template:

mixin template sumDiff(T, R){
   R opBinary(string op)(T rhs) const
   if (op == "+" || op == "-"){
      return mixin("R(x " ~ op ~ " rhs.x, y " ~ op ~ "rhs.y, z " ~ op ~ " rhs.z)");
   }
}

but the same error occurs.

If I don't use the mixin templates and, instead, I overload the operations into the structures everything works.

struct point{
   float x, y, z;

   vec opBinary(string op)(point rhs) const
   if (op == "-") {
      return vec(x - rhs.x, y - rhs.y, z - rhs.z);
   }

   point opBinary(string op)(vec rhs) const
   if (op == "+") {
      return point(x + rhs.x, y + rhs.y, z + rhs.z);
   }
}

struct vec{
   float x, y, z;
}

void main(){
   point p1 = {1,2,3}, p2 = {5,6,7};
   vec v1 = p1-p2;

   vec v2 = {2,-1,0};
   point p3 = p1+v2;
}

Am I doing something wrong or it is impossible to use mixin templates like that?

Looks like a compiler bug, since substituting the real methods makes the mixin template code compile fine

Also, remove the const, otherwise the method will word only on const instances even if the bug gets fixed

mixin template sum(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "+"){
      return R(x + rhs.x, y + rhs.y, z + rhs.z);
   }
}

mixin template diff(T, R) {
   R opBinary(string op)(T rhs) const
   if (op == "-") {
      return R(x - rhs.x, y - rhs.y, z - rhs.z);
   }
}

struct point{
   float x, y, z;
   mixin diff!(point, vec);
   mixin sum!(vec, point);
}

struct vec{
   float x, y, z;
}

void main(){
   point p1 = {1,2,3}, p2 = {5,6,7};
   vec v1 = p1.opBinary!"-"(p2);   // made - explicit

   vec v2 = {2,-1,0};
   point p3 = p1.opBinary!"+"(v2);  // made + explicit
}
April 06, 2022
On Wednesday, 6 April 2022 at 10:36:04 UTC, francesco.andreetto wrote:
> Am I doing something wrong or it is impossible to use mixin templates like that?

mixin templates can't bring in operator overloads. The spec doesn't really say this I think, but that's the way it has been for a long time.

It only checks for operator overloads on the direct thing itself, no UFCS, no mixin template.
April 06, 2022

On 4/6/22 6:36 AM, francesco.andreetto wrote:

>

I have two structs, point and vec, in which I want to implement some arithmetic operations.
The operations are:

point - point = vec
point + vec = point

Using mixin templates the code compiles but calling the operations in the main causes an "incompatible type" Error:

This seems like a bug in the compiler. You can mixin operator overloads using one mixin template, but not multiple.

e.g. raylib-d uses this:

https://github.com/schveiguy/raylib-d/blob/89733bab9fd1d3588c14f4aa54b62ad45022a105/source/raymathext.d#L75

>

I tried to implement a single template:

mixin template sumDiff(T, R){
    R opBinary(string op)(T rhs) const
    if (op == "+" || op == "-"){
       return mixin("R(x " ~ op ~ " rhs.x, y " ~ op ~ "rhs.y, z " ~ op ~ " rhs.z)");
    }
}

but the same error occurs.

This is different from your original. If I do this, it works:

mixin template both(T, R) {
   R opBinary(string op : "+")(T rhs) const
   {
      return R(x + rhs.x, y + rhs.y, z + rhs.z);
   }

   T opBinary(string op : "-")(R rhs) const
   {
      return T(x - rhs.x, y - rhs.y, z - rhs.z);
   }
}

struct point{
   float x, y, z;
   mixin both!(vec, point);
}
>

If I don't use the mixin templates and, instead, I overload the operations into the structures everything works.

It also works to call opBinary directly:

vec v1 = p1.opBinary!"-"(p2);

Which leads me to believe it's not an ambiguity error, but rather just a straight omission on how the operator overloading works.

I think you should file a bug.

-Steve

April 06, 2022

On 4/6/22 12:52 PM, Adam D Ruppe wrote:

>

On Wednesday, 6 April 2022 at 10:36:04 UTC, francesco.andreetto wrote:

>

Am I doing something wrong or it is impossible to use mixin templates like that?

mixin templates can't bring in operator overloads. The spec doesn't really say this I think, but that's the way it has been for a long time.

It only checks for operator overloads on the direct thing itself, no UFCS, no mixin template.

As I mentioned elsewhere, it does work. But the situation I think must be that it's only one mixin template. Probably also you can't have any overloads in the type itself.

Note that I also think string mixins would work perfectly fine. And UFCS will never work for operator overloads, but that is by design.

-Steve

April 06, 2022
On Wednesday, 6 April 2022 at 17:33:28 UTC, Steven Schveighoffer wrote:
> As I mentioned elsewhere, it does work. But the situation I think must be that it's only one mixin template. Probably also you can't have any overloads in the type itself.

ooooooooooh yeah there's multiple here so the names don't overload and one on the time itself would override the ones from mixin template.

You'd have to alias them together on the type.
April 12, 2022

On Wednesday, 6 April 2022 at 16:36:51 UTC, Tejas wrote:

>

Looks like a compiler bug, since substituting the real methods makes the mixin template code compile fine

Hi Tejas,
Thank you very much for your answer!

>

Also, remove the const, otherwise the method will word only on const instances even if the bug gets fixed

And thank you for this advice: I'll definitely remove the const!

FA

April 12, 2022

On Wednesday, 6 April 2022 at 17:33:28 UTC, Steven Schveighoffer wrote:

>

As I mentioned elsewhere, it does work. But the situation I think must be that it's only one mixin template. Probably also you can't have any overloads in the type itself.

Note that I also think string mixins would work perfectly fine. And UFCS will never work for operator overloads, but that is by design.

-Steve

Hello Steve,

Thank you very much for your answers, you have been very clear and helpful!

-FA

April 12, 2022
On Wednesday, 6 April 2022 at 17:59:12 UTC, Adam D Ruppe wrote:

> ooooooooooh yeah there's multiple here so the names don't overload and one on the time itself would override the ones from mixin template.
>
> You'd have to alias them together on the type.

Hello Adam D Ruppe,

I see what you mean,
thank you very much for your answers!

-FA