Thread overview
Can i rewrite methods in one line?
Sep 03, 2012
Ivan Agafonov
Sep 03, 2012
Ali Çehreli
Sep 03, 2012
Ivan Agafonov
Sep 03, 2012
Ali Çehreli
Sep 03, 2012
Ivan Agafonov
Sep 03, 2012
Ivan Agafonov
Sep 03, 2012
Ivan Agafonov
September 03, 2012
struct Vector(T, uint size)
{
	static assert(size >= 2 && size <= 4);
	static assert(__traits(isFloating, T);
	
	/// Vector components
	union
	{
		T[size] array = 0;
		struct
		{
			static if(size == 1) T x;
			static if(size == 2) T x, y;
			static if(size == 3) T x, y, z;
			static if(size == 4) T x, y, z, w;
		}
	}

	this (T rhs)			{ array[] = rhs; }
	this (T[] components...)	{ array[] = components[]; }
	this (T* components)		{ array[] = components[0..size]; }
	this (T[size] components)	{ array[] = components[]; }
	this (Vector rhs)		{ array[] = rhs.array[]; }
	
	Vector opUnary(string op) () if (op == "-")
	{
		Vector tmp;
		tmp.array[] = -array[];
		return tmp;
	}

	Vector opBinary(string op) (Vector rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{
		Vector tmp;
		tmp.array[] = mixin("array[] "~op~" rhs.array[]");
		return tmp;
	}
	
	Vector opBinary(string op) (T rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{
		Vector tmp;
		tmp.array[] = mixin("array[] "~op~" rhs");
		return tmp;
	}

	ref Vector opOpAssign(string op) (Vector rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{
		mixin("array[] "~op~"= rhs.array[];");
		return this;
	}

	ref Vector opOpAssign(string op) (T rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{
		mixin("array[] "~op~"= rhs;");
		return this;
	}
}

I want to rewrite it in something like this:

struct Vector(T, uint size)
{
	static assert(size >= 2 && size <= 4);
	static assert(__traits(isFloating, T);
	
	/// Vector components
	union
	{
		T[size] array = 0;
		struct
		{
			static if(size == 1) T x;
			static if(size == 2) T x, y;
			static if(size == 3) T x, y, z;
			static if(size == 4) T x, y, z, w;
		}
	}

	this (T rhs)			{ array[] = rhs; }
	this (T[] components...)	{ array[] = components[]; }
	this (T* components)		{ array[] = components[0..size]; }
	this (T[size] components)	{ array[] = components[]; }
	this (Vector rhs)		{ array[] = rhs.array[]; }
	
	Vector opUnary(string op) () if (op == "-")
	{ return Vector(-array); }

	Vector opBinary(string op) (Vector rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{ return Vector(mixin("array[] "~op~" rhs.array[]")); }
	
	Vector opBinary(string op) (T rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{ return Vector(mixin("array[] "~op~" rhs")); }

	ref Vector opOpAssign(string op) (Vector rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{ return Vector(mixin("array[] "~op~"= rhs.array[]")); }

	ref Vector opOpAssign(string op) (T rhs)
		if(op == "+" || op == "-" || op == "*" || op == "/")
	{ return Vector(mixin("array[] "~op~"= rhs")); }
}

Main goal is to return and constuct in one place. Can I do so?
I now that there will take place compiller optimisations and both versions will be the same, but what if not? PS: Sorry for my english.
September 03, 2012
In general, yes, you can construct and return at the same time. As you said, in a language like D where source code is almost always visible, the compiler can apply many optimization techniques.

However, some of your operators ended up having semantic differences. opOpAssign used to return a reference to this object:

    ref Vector opOpAssign(string op) (Vector rhs)
        if(op == "+" || op == "-" || op == "*" || op == "/")
    {
        mixin("array[] "~op~"= rhs.array[];");
        return this;
    }

opOpAssign now returns a reference to a newly-created temporary object:

    ref Vector opOpAssign(string op) (Vector rhs)
        if(op == "+" || op == "-" || op == "*" || op == "/")
    { return Vector(mixin("array[] "~op~"= rhs.array[]")); }

(It is the same for the other opOpAssign.)

Ali
September 03, 2012
On Monday, 3 September 2012 at 02:40:09 UTC, Ali Çehreli wrote:
> In general, yes, you can construct and return at the same time. As you said, in a language like D where source code is almost always visible, the compiler can apply many optimization techniques.
>
> However, some of your operators ended up having semantic differences. opOpAssign used to return a reference to this object:
>
>     ref Vector opOpAssign(string op) (Vector rhs)
>         if(op == "+" || op == "-" || op == "*" || op == "/")
>     {
>         mixin("array[] "~op~"= rhs.array[];");
>         return this;
>     }
>
> opOpAssign now returns a reference to a newly-created temporary object:
>
>     ref Vector opOpAssign(string op) (Vector rhs)
>         if(op == "+" || op == "-" || op == "*" || op == "/")
>     { return Vector(mixin("array[] "~op~"= rhs.array[]")); }
>
> (It is the same for the other opOpAssign.)
>
> Ali

I made a mistake here. This operators have already "onelined" :

ref Vector opAssign(Vector rhs) { array[] = rhs.array[]; return this; }
ref Vector opAssign(T[size] rhs) { array[] = rhs[]; return this; }
	
ref Vector opOpAssign(string op) (T[size] rhs) if(op == "+" || op == "-" || op == "*" || op == "/")
{ mixin("array[] "~op~"= rhs[];"); return this; }
	
ref Vector opOpAssign(string op) (Vector rhs) if(op == "+" || op == "-" || op == "*" || op == "/")
{ mixin("array[] "~op~"= rhs.array[];"); return this; }
	
ref Vector opOpAssign(string op) (T rhs) if(op == "+" || op == "-" || op == "*" || op == "/")
{ mixin("array[] "~op~"= rhs;"); return this; }

But i can't write something like this:
return Vector(mixin("array[] "~op~"= rhs.array[]"));
because array[] op= rhs.array[] is not a right expression it does not return an array, it can only be used in assigment to slice like this[]. And expression array op= rhs.array does not make sense too.

September 03, 2012
Yeah! I did it!
How about this solution? What do you think?

static assert(size >= 2 && size <= 4);
	static assert(__traits(isFloating, T));
	
	/// Vector components
	union
	{
		T[size] array = 0;
		struct
		{
			static if(size == 2) T x, y;
			static if(size == 3) T x, y, z;
			static if(size == 4) T x, y, z, w;
		}
	}

	this (T rhs)			{ array[] = rhs; }
	this (T[] components...)	{ array[] = components[]; }
	this (T* components)		{ array[] = components[0..size]; }
	this (T[size] components)	{ array[] = components[]; }
	this (Vector rhs)		{ array[] = rhs.array[]; }
	
	private static bool isMathOp(string op)
	{ return (op == "+" || op == "-" || op == "*" || op == "/"); }
	
	Vector opUnary(string op) () if (op == "-")
	{ return Vector(array.dup[] = -array[]); }

	Vector opBinary(string op) (Vector rhs) if(isMathOp(op))
	{ return Vector(mixin("array.dup[] "~op~"= rhs.array[]")); }
	
	Vector opBinary(string op) (T rhs) if(isMathOp(op))
	{ return Vector(mixin("array.dup[] "~op~"= rhs")); }

	ref Vector opAssign(Vector rhs) { array[] = rhs.array[]; return this; }
	ref Vector opAssign(T[size] rhs) { array[] = rhs[]; return this; }
	
	ref Vector opOpAssign(string op) (T[size] rhs) if(isMathOp(op))
	{ mixin("array[] "~op~"= rhs[];"); return this; }
	
	ref Vector opOpAssign(string op) (Vector rhs) if(isMathOp(op))
	{ mixin("array[] "~op~"= rhs.array[];"); return this; }
	
	ref Vector opOpAssign(string op) (T rhs) if(isMathOp(op))
	{ mixin("array[] "~op~"= rhs;"); return this; }

	... Some other op's ...
}
September 03, 2012
On 09/02/2012 07:57 PM, Ivan Agafonov wrote:

> ref Vector opOpAssign(string op) (T rhs) if(op == "+" || op == "-" || op
> == "*" || op == "/")
> { mixin("array[] "~op~"= rhs;"); return this; }
>
> But i can't write something like this:
> return Vector(mixin("array[] "~op~"= rhs.array[]"));

You meant it for opOpAssign, right?

opOpAssign must 'return this', not a new object. Otherwise chaining operators would be confusing:

    (v0 += v1) *= v2;

If the += operation could return a new object (which it can't), then *= would be operating on that.

So your existing version is correct:

    ref Vector opOpAssign(string op) (T[size] rhs)
        if(op == "+" || op == "-" || op == "*" || op == "/")
    { mixin("array[] "~op~"= rhs[];"); return this; }

But if you don't care about chained operations you can also return void:

    void opOpAssign(string op) (T[size] rhs)
        if(op == "+" || op == "-" || op == "*" || op == "/")
    { mixin("array[] "~op~"= rhs[];"); }

Ali

September 03, 2012
Note that opBinary operators uses op= instead of op :

Vector opBinary(string op) (Vector rhs) if(isMathOp(op))
{ return Vector(mixin("array.dup[] "~op~"= rhs.array[]")); }

Vector opBinary(string op) (T rhs) if(isMathOp(op))
{ return Vector(mixin("array.dup[] "~op~"= rhs")); }
September 03, 2012
Note that opBinary operators uses op= instead of op :

Vector opBinary(string op) (Vector rhs) if(isMathOp(op))
{ return Vector(mixin("array.dup[] "~op~"= rhs.array[]")); }

Vector opBinary(string op) (T rhs) if(isMathOp(op))
{ return Vector(mixin("array.dup[] "~op~"= rhs")); }