Thread overview
Operator Overloading with multiple return types
Mar 15, 2019
eXodiquas
Mar 15, 2019
Sebastiaan Koppe
Mar 15, 2019
Ali Çehreli
Mar 15, 2019
eXodiquas
Mar 15, 2019
drug
Mar 15, 2019
H. S. Teoh
Mar 15, 2019
Ali Çehreli
Mar 15, 2019
H. S. Teoh
Mar 15, 2019
H. S. Teoh
March 15, 2019
Hi everyone,

i'm currently working on a small physics engine and I thought it would be a nice feature to overload the operators of my vector struct so I don't have to make ugly function calls just to add and "multiply" my vectors. The problem now is that overloading the addition and subtraction of my vector struct is straight forward because both return and take a vector, dot product is not working for me because it is not possible to overload a function by return type (at least not that I am aware of).

My code looks like the one from the dlang docs:

Vector opBinary(string op)(Vector rhs)
{
    static if (op == "+") return Vector(this.x + rhs.x, this.y + rhs.y);
    else static if (op == "-") return Vector(this.x - rhs.x, this.y - rhs.y);
}

As you can see for the dot product the return type has to be a float/double and not a vector. Is there any way to achive this behaivour with D2? The opMul() function is not D2 style and I don't want to use it.

Thank you very much,

eXodiquas
March 15, 2019
On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote:
> Is there any way to achive this behaivour with D2?

Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch.
March 15, 2019
On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote:
> On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote:
>> Is there any way to achive this behaivour with D2?
> 
> Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch.

Or use template constraints:


struct Vector {
  Vector opBinary(string op)(Vector rhs)
    if (op == "+") {
      return Vector();
    }

  double opBinary(string op)(Vector rhs)
    if (op == "/") {
      return 0.5;
    }
}

Ali
March 15, 2019
On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote:
> On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote:
>> On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote:
>>> Is there any way to achive this behaivour with D2?
>> 
>> Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch.
>
> Or use template constraints:
>
>
> struct Vector {
>   Vector opBinary(string op)(Vector rhs)
>     if (op == "+") {
>       return Vector();
>     }
>
>   double opBinary(string op)(Vector rhs)
>     if (op == "/") {
>       return 0.5;
>     }
> }
>
> Ali

Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong?

Vector2 opBinary(string op)(Vector2 rhs) {
        if (op == "+") {
            return Vector2(this.x + rhs.x, this.y + rhs.y);
        } else if (op == "-") {
            return Vector2(this.x - rhs.x, this.y - rhs.y);
        }
    }

    float opBinary(string op)(Vector2 rhs) {
        if (op == "*") {
            return this.x * rhs.x + this.y * rhs.y;
        }
    }

This gives me the error:
overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for opBinary

eXodiquas
March 16, 2019
16.03.2019 1:30, eXodiquas пишет:
> On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote:
>> On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote:
>>> On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote:
>>>> Is there any way to achive this behaivour with D2?
>>>
>>> Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch.
>>
>> Or use template constraints:
>>
>>
>> struct Vector {
>>   Vector opBinary(string op)(Vector rhs)
>>     if (op == "+") {
>>       return Vector();
>>     }
>>
>>   double opBinary(string op)(Vector rhs)
>>     if (op == "/") {
>>       return 0.5;
>>     }
>> }
>>
>> Ali
> 
> Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong?
> 
> Vector2 opBinary(string op)(Vector2 rhs) {
>          if (op == "+") {
>              return Vector2(this.x + rhs.x, this.y + rhs.y);
>          } else if (op == "-") {
>              return Vector2(this.x - rhs.x, this.y - rhs.y);
>          }
>      }
> 
>      float opBinary(string op)(Vector2 rhs) {
>          if (op == "*") {
>              return this.x * rhs.x + this.y * rhs.y;
>          }
>      }
> 
> This gives me the error:
> overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for opBinary
> 
> eXodiquas
You add wrong braces (`if` here is part of method signature not its body):
```
      Vector2 opBinary(string op)(Vector2 rhs) if (op == "+")
      {
          return Vector2(this.x + rhs.x, this.y + rhs.y);
      }

      Vector2 opBinary(string op)(Vector2 rhs) if (op == "-")
      {
          return Vector2(this.x - rhs.x, this.y - rhs.y);
      }

      float opBinary(string op)(Vector2 rhs) if (op == "*")
      {
          return this.x * rhs.x + this.y * rhs.y;
      }
```
March 15, 2019
On Fri, Mar 15, 2019 at 09:35:12PM +0000, eXodiquas via Digitalmars-d-learn wrote: [...]
> Vector opBinary(string op)(Vector rhs)
> {
>     static if (op == "+") return Vector(this.x + rhs.x, this.y + rhs.y);
>     else static if (op == "-") return Vector(this.x - rhs.x, this.y -
> rhs.y);
> }
> 
> As you can see for the dot product the return type has to be a float/double and not a vector. Is there any way to achive this behaivour with D2? The opMul() function is not D2 style and I don't want to use it.
[...]

Use signature constraints to declare different overloads depending on what the operator is. For example:

	Vector opBinary(string op)(Vector rhs)
		if (op == "+" || op == "=")
	{
		... /* implement + and - here */
	}

	double opBinary(string op)(Vector rhs)
		if (op == "*")
	{
		... /* implement dot product here */
	}


T

-- 
It is widely believed that reinventing the wheel is a waste of time; but I disagree: without wheel reinventers, we would be still be stuck with wooden horse-cart wheels.
March 15, 2019
On Fri, Mar 15, 2019 at 10:30:41PM +0000, eXodiquas via Digitalmars-d-learn wrote:
> On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote:
[...]
> > Or use template constraints:
> > 
> > struct Vector {
> >   Vector opBinary(string op)(Vector rhs)
> >     if (op == "+") {
> >       return Vector();
> >     }
> > 
> >   double opBinary(string op)(Vector rhs)
> >     if (op == "/") {
> >       return 0.5;
> >     }
> > }
> > 
> > Ali
> 
> Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong?
> 
> Vector2 opBinary(string op)(Vector2 rhs) {
>         if (op == "+") {
>             return Vector2(this.x + rhs.x, this.y + rhs.y);
>         } else if (op == "-") {
>             return Vector2(this.x - rhs.x, this.y - rhs.y);
>         }
>     }
> 
>     float opBinary(string op)(Vector2 rhs) {
>         if (op == "*") {
>             return this.x * rhs.x + this.y * rhs.y;
>         }
>     }
> 
> This gives me the error:
> overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for
> opBinary
[...]

Ali's example was unfortunately deceptively formatted. The `if` has to be *outside* the function body; it's not a regular if-statement, but a signature constraint. And there is no `else` clause to it.

	Vector opBinary(string op)(Vector rhs)
		if (op == '+' || op == '-')
	{
		/* function body begins here */
		...
	}

	double opBinary(string op)(Vector rhs)
		if (op == '*')
	{
		/* function body begins here */
		...
	}


T

-- 
Notwithstanding the eloquent discontent that you have just respectfully expressed at length against my verbal capabilities, I am afraid that I must unfortunately bring it to your attention that I am, in fact, NOT verbose.
March 15, 2019
On 03/15/2019 03:48 PM, H. S. Teoh wrote:
> On Fri, Mar 15, 2019 at 10:30:41PM +0000, eXodiquas via Digitalmars-d-learn wrote:
>> On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote:
> [...]
>>> Or use template constraints:
>>>
>>> struct Vector {
>>>    Vector opBinary(string op)(Vector rhs)
>>>      if (op == "+") {
>>>        return Vector();
>>>      }
>>>
>>>    double opBinary(string op)(Vector rhs)
>>>      if (op == "/") {
>>>        return 0.5;
>>>      }
>>> }
>>>
>>> Ali
>>
>> Thanks for the quick and simple answers, but I don't get this one. If
>> I do it that way the compiler doesn't know which function to call, or
>> am I doing something wrong?
>>
>> Vector2 opBinary(string op)(Vector2 rhs) {
>>          if (op == "+") {
>>              return Vector2(this.x + rhs.x, this.y + rhs.y);
>>          } else if (op == "-") {
>>              return Vector2(this.x - rhs.x, this.y - rhs.y);
>>          }
>>      }
>>
>>      float opBinary(string op)(Vector2 rhs) {
>>          if (op == "*") {
>>              return this.x * rhs.x + this.y * rhs.y;
>>          }
>>      }
>>
>> This gives me the error:
>> overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for
>> opBinary
> [...]
> 
> Ali's example was unfortunately deceptively formatted.

My editor did that. :)

On my work computer, I've been experimenting with pulling the 'if', 'in', etc to the same level as the function signature:

  int foo(T)(T t)
  if (isBlah!T)
  in (!t.empty) {
    // ...
  }

Ali
March 15, 2019
On Fri, Mar 15, 2019 at 04:29:22PM -0700, Ali Çehreli via Digitalmars-d-learn wrote:
> On 03/15/2019 03:48 PM, H. S. Teoh wrote:
[...]
> > Ali's example was unfortunately deceptively formatted.
> 
> My editor did that. :)

This is why I don't trust auto-formatters. ;-)


> On my work computer, I've been experimenting with pulling the 'if', 'in', etc to the same level as the function signature:
> 
>   int foo(T)(T t)
>   if (isBlah!T)
>   in (!t.empty) {
>     // ...
>   }
[...]

Phobos style also dictates that:

	int foo(T)(T t)
	if (isBlah!T)
	in (!t.empty)
	{
		...
	}

In my own code, however, I find it too block-of-text-y, so I prefer to indent it inwards:

	int foo(T)(T t)
		if (isBlah!T)
		in (!t.empty)
	{
		...
	}

But if your style puts { at the end of the line rather than the beginning, this could make it even easier to confuse for a statement inside the body.

So YMMV.


T

-- 
IBM = I'll Buy Microsoft!