www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Getting resulting/return type from operation in templates

reply Rekel <paultjeadriaanse gmail.com> writes:
I recently found __traits(compiles, ...) can be used in template 
constraints.
(I first used `is(mixin("T.init"~op~"T2.init"))` but this cause 
problems related to nanF)

However I'm wondering if there is an easier way to do what I'm 
currently doing, especially since using typeof inside the result 
declaration is quite cumbersome. This is a bigger problem in my 
less-simplified code (using matrices), where it starts getting 
slightly unreadable.
It also assumes the presence of .vec[0] (not necessarily a 
problem here but it seems like a codesmell)

Simplified:
```d
template supported(A, string op, B) {
	const bool supported = __traits(compiles, (A a, B b) {
		mixin("return a" ~ op ~ "b;");
	});
}

struct Vec(T, uint L) {
	T[L] vec;
	alias vec this;

	auto opBinary(string op, R:
			T2[], T2)(R right) const if (supported!(T, op, T2)) {
		Vec!(typeof(mixin("this[0]" ~ op ~ "right[0]")), L) result;
		static foreach (i; 0 .. L) {
			mixin("result[i] = this[i] " ~ op ~ " right[i];");
		}
		return result;
	}

	auto opBinary(string op, R)(R right) const if (supported!(T, op, 
R)) {
		Vec!(typeof(mixin("this[0]" ~ op ~ "right")), L) result;
		static foreach (i; 0 .. L) {
			mixin("result[i] = this[i] " ~ op ~ " right;");
		}
		return result;
	}
}

void main(string[] args) {
	Vec!(float, 4) v = Vec!(float, 4)([0, 1, 2, 3]);
	auto w = v * 2.0;
	auto x = v + v;
	auto y = v + [1, 1, 1, 1];
	pragma(msg, typeof(w)); // prints "Vec!(double, 4u)"
	pragma(msg, typeof(x)); // prints "Vec!(float, 4u)"
	pragma(msg, typeof(y)); // prints "Vec!(float, 4u)"
}
```
Jul 06
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 6 July 2021 at 14:12:48 UTC, Rekel wrote:
 I recently found __traits(compiles, ...) can be used in 
 template constraints.
 (I first used `is(mixin("T.init"~op~"T2.init"))` but this cause 
 problems related to nanF)

 However I'm wondering if there is an easier way to do what I'm 
 currently doing, especially since using typeof inside the 
 result declaration is quite cumbersome. This is a bigger 
 problem in my less-simplified code (using matrices), where it 
 starts getting slightly unreadable.
 It also assumes the presence of .vec[0] (not necessarily a 
 problem here but it seems like a codesmell)

 Simplified:
 ```d
 template supported(A, string op, B) {
 	const bool supported = __traits(compiles, (A a, B b) {
 		mixin("return a" ~ op ~ "b;");
 	});
 }
 ```
Instead of having the template evaluate to a `bool`, have it evaluate to the type of the result: ```d alias ResultType(Lhs, string op, Rhs) = typeof(((Lhs lhs, Rhs rhs) => mixin("lhs", op, "rhs"))()); static assert(is(ResultType!(int, "+", double) == double)); static assert(is(ResultType!(string, "~", string) == string)); static assert(!is(ResultType!(string, "+", int))); // no valid result type ``` Then you can use `is()` in the template constraints to check whether the operation is supported: ```d auto opBinary(string op, Rhs: T2[], T2) const if (is(ResultType!(T, op, T2))) { Vec!(ResultType!(T, op, T2), L) result; // etc. } ```
Jul 06
parent reply Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 6 July 2021 at 14:27:35 UTC, Paul Backus wrote:
 Instead of having the template evaluate to a `bool`, have it 
 evaluate to the type of the result:

 ```d
 alias ResultType(Lhs, string op, Rhs) =
     typeof(((Lhs lhs, Rhs rhs) => mixin("lhs", op, "rhs"))());

 static assert(is(ResultType!(int, "+", double) == double));
 static assert(is(ResultType!(string, "~", string) == string));
 static assert(!is(ResultType!(string, "+", int))); // no valid 
 result type
 ```

 Then you can use `is()` in the template constraints to check 
 whether the operation is supported:

 ```d
 auto opBinary(string op, Rhs: T2[], T2) const
     if (is(ResultType!(T, op, T2)))
 {
     Vec!(ResultType!(T, op, T2), L) result;
     // etc.
 }
 ```
Oh that's neat, thanks. I guess that would make it: ```d auto opBinary(string op, R)(R right) const if (is(ResultType!(T, op, R))) { Vec!(ResultType!(T, op, R), L) result; static foreach (i; 0 .. L) { mixin("result[i] = this[i] " ~ op ~ " right;"); } return result; } ``` I guess I'll just have to take the result definition for what it is. At least the return type can use auto, haha. Imagine using ResultType!(T, op, R) thrice... Is there any reason the function call in the alias is acceptable? I would imagine the () part would actually require 2 parameters.
Jul 06
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 6 July 2021 at 14:43:26 UTC, Rekel wrote:
 Is there any reason the function call in the alias is 
 acceptable? I would imagine the () part would actually require 
 2 parameters.
You're right, it does; that was a mistake on my part. It should work with `Lhs.init` and `Rhs.init` as arguments.
Jul 06
parent Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 6 July 2021 at 15:00:24 UTC, Paul Backus wrote:
 On Tuesday, 6 July 2021 at 14:43:26 UTC, Rekel wrote:
 Is there any reason the function call in the alias is 
 acceptable? I would imagine the () part would actually require 
 2 parameters.
You're right, it does; that was a mistake on my part. It should work with `Lhs.init` and `Rhs.init` as arguments.
Hmm . . . doing that ironically brings me back to using: ```d alias ResultType(Lhs, string op, Rhs) = typeof(mixin("Lhs.init" ~ op ~ "Rhs.init")); auto opBinary(string op, R: T2[], T2)(R right) const if (is(ResultType!(T, op, T2))) { Vec!(ResultType!(T, op, T2), L) result; static foreach (i; 0 .. L) { mixin("result[i] = this[i] " ~ op ~ " right[i];"); } return result; } ``` Kind of ironic this is basically an alias of my old solution. Strangely enough the nanF issue went away though, I guess I misinterpreted this. Thanks for your help anyway 😅
Jul 06