www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Mixin Templates and Operators

reply francesco.andreetto <francesco.andreetto97 gmail.com> writes:
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:

```d
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:

```d
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.

```d
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?
Apr 06 2022
next sibling parent reply Tejas <notrealemail gmail.com> writes:
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:

 ```d
 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:

 ```d
 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.

 ```d
 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 ```d 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 }
Apr 06 2022
parent francesco.andreetto <francesco.andreetto97 gmail.com> writes:
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
Apr 12 2022
prev sibling next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
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.
Apr 06 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 06 2022
next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
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.
Apr 06 2022
parent francesco.andreetto <francesco.andreetto97 gmail.com> writes:
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
Apr 12 2022
prev sibling parent francesco.andreetto <francesco.andreetto97 gmail.com> writes:
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
Apr 12 2022
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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:
 
 ```d
 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: ```d 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: ```d 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
Apr 06 2022