digitalmars.D.learn - Getting resulting/return type from operation in templates
- Rekel (48/48) Jul 06 2021 I recently found __traits(compiles, ...) can be used in template
- Paul Backus (21/40) Jul 06 2021 Instead of having the template evaluate to a `bool`, have it
- Rekel (18/38) Jul 06 2021 Oh that's neat, thanks.
- Paul Backus (3/6) Jul 06 2021 You're right, it does; that was a mistake on my part. It should
- Rekel (18/24) Jul 06 2021 Hmm . . . doing that ironically brings me back to using:
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 2021
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 2021
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 2021
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 2021
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: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 😅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 2021