digitalmars.D.learn - opCast / operator overloading with additional template arguments
- Paul (15/22) Jan 10 2021 Is there a way to have additional template arguments in operator
- =?UTF-8?Q?Ali_=c3=87ehreli?= (24/25) Jan 10 2021 overloads?
- Paul (5/38) Jan 10 2021 Thank you, sadly S (S2 now) is not any specific type, sorry I'll
- =?UTF-8?Q?Ali_=c3=87ehreli?= (51/53) Jan 10 2021 The is expression can be so complicated that I used a different approach...
- =?UTF-8?Q?Ali_=c3=87ehreli?= (3/5) Jan 10 2021 I take that back. Yes, it should be S2. (I've been off lately. :) )
- Paul (3/10) Jan 10 2021 Is it ok to use .init even though we don't need an instantiated
- =?UTF-8?Q?Ali_=c3=87ehreli?= (10/19) Jan 11 2021 Yes, .init useful in many templates. However, you don't actually use=20
- Steven Schveighoffer (10/34) Jan 10 2021 It has nothing to do with operator overloads. types inferred inside
- Paul (15/20) Jan 10 2021 Oh wouw, this seems to work perfectly! Awesome thanks ^^
- Paul Backus (8/21) Jan 10 2021 The compiler does not look at template constraints until after it
Is there a way to have additional template arguments in operator overloads? The problem I'm having is mostly caused by casting a templated struct to other templated structs. I had the following code;T opCast(T)() const if (is(T : Vec!(vecsize, S), S)) { T converted; static foreach (i; 0 .. vecsize) converted.content[i] = cast(S) content[i]; return converted; }When I try to use this, I get the error 'undefined identifier S'. Alternatively using:T opCast(T, S)() . . .causes the error 'template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()' I found using 'alias S = typeof(content[0])' works as a kind of ducttape alternative, however this seems like a codesmell to me, and I fear it won't scale well either. Am I missing a simple solution? And why is there no automatic argument deduction in this scenario when compared to normal function calls? (if the above opcast is translated to 'foo.opCast!T`)
Jan 10 2021
On 1/10/21 4:09 PM, Paul wrote:Is there a way to have additional template arguments in operatoroverloads? I haven't tried that but the following method seems to work for you. You don't show complete code; so, I hope I came up with something that reflects your case. import std; struct S(T) { auto opCast(U)() const if (isInstanceOfS!U) { writefln!"Converting from %s to %s"(S.stringof, U.stringof); return U.init; } } enum isInstanceOfS(T) = isInstanceOf!(S, T); void main() { auto a = S!int(); auto b = cast(S!double)a; // Works auto c = a.to!(S!double); // This works too } I needed the isInstanceOfS wrapper over std.traits.isInstanceOf because I could not use isInstanceOf inside the definition of S because the symbol 'S' does not mean "the template S", but the specific instantiation of it e.g. S!int. Outside, S means "the template S". Ali
Jan 10 2021
On Monday, 11 January 2021 at 00:25:36 UTC, Ali Çehreli wrote:You don't show complete code; so, I hope I came up with something that reflects your case.Thank you, sadly S (S2 now) is not any specific type, sorry I'll paste more of my file, I hope that's ok. (Sidenote, I'm not sure it's the most elegant approach to have a templated union like this, and I left out some unnecessary stuff like 'opBinary')version (HoekjeD_Double) { private alias standard_accuracy = double; } else { private alias standard_accuracy = float; } struct Vec(int size, S = standard_accuracy) { union { S[size] content; static if (size >= 1) { struct { S x; static if (size >= 2) { S y; static if (size >= 3) { S z; static if (size >= 4) S w; } } } } } T opCast(T)() const if (is(T : Vec!(size, S2), S2)) { T converted; static foreach (i; 0 .. size) converted.content[i] = cast(S2) content[i]; return converted; } }
Jan 10 2021
On 1/10/21 5:09 PM, Paul wrote:I'll paste more of my file, I hope that's ok.Not only ok but much appreciated. :)The is expression can be so complicated that I used a different approach below. I left notes in capital letters below. Especially cast(S2) looks wrong to me: import std; version (HoekjeD_Double) { private alias standard_accuracy = double; } else { private alias standard_accuracy = float; } struct Vec(int size, S = standard_accuracy) { // You could add this instead of getting the type from // 'content' as in how S2 is aliased in opCast below: // // alias AccuracyType = S; union { S[size] content; static if (size >= 1) { struct { S x; static if (size >= 2) { S y; static if (size >= 3) { S z; static if (size >= 4) S w; } } } } } T opCast(T)() const // CHANGED: if (isInstanceOfVec!T && T.init.content.length == size) { // ADDED: alias S2 = typeof(T.init.content[0]); T converted; static foreach (i; 0 .. size) // OBSERVATION: Should the cast below be S? converted.content[i] = cast(S2) content[i]; return converted; } } enum isInstanceOfVec(T) = isInstanceOf!(Vec, T); void main() { auto a = Vec!(42, float)(); auto b = a.to!(Vec!(42, double)); } AliT opCast(T)() const if (is(T : Vec!(size, S2), S2)) {
Jan 10 2021
On 1/10/21 6:37 PM, Ali =C3=87ehreli wrote:// OBSERVATION: Should the cast below be S? converted.content[i] =3D cast(S2) content[i];I take that back. Yes, it should be S2. (I've been off lately. :) ) Ali
Jan 10 2021
On Monday, 11 January 2021 at 02:37:24 UTC, Ali Çehreli wrote:T opCast(T)() const if (is(T : Vec!(size, S2), S2)) {The is expression can be so complicated that I used a different approach below.if (isInstanceOfVec!T && T.init.content.length == size) { // ADDED: alias S2 = typeof(T.init.content[0]);Is it ok to use .init even though we don't need an instantiated value?
Jan 10 2021
On 1/10/21 7:27 PM, Paul wrote:On Monday, 11 January 2021 at 02:37:24 UTC, Ali =C3=87ehreli wrote:Yes, .init useful in many templates. However, you don't actually use=20 that object because typeof does not evaluate the expression, just=20 produces the type of it. Related, the content[0] expression might be seen as invalid code because = the array may not have any elements at all but again, there is no access = to element 0. There are other ways e.g. import std.range; alias S2 =3D ElementType!(typeof(T.init.content)); AliT opCast(T)() const if (is(T : Vec!(size, S2), S2)) {The is expression can be so complicated that I used a different approach below.if (isInstanceOfVec!T && T.init.content.length =3D=3D size) { // ADDED: alias S2 =3D typeof(T.init.content[0]);Is it ok to use .init even though we don't need an instantiated value?=
Jan 11 2021
On 1/10/21 7:09 PM, Paul wrote:Is there a way to have additional template arguments in operator overloads? The problem I'm having is mostly caused by casting a templated struct to other templated structs. I had the following code;It has nothing to do with operator overloads. types inferred inside template constraints are not accessible inside the function. The awkward solution is to use the same is expression inside the function: static if(is(T : Vec!(vecsize, S), S)) {} // now you can use S I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)() But I don't have your full code to test. -SteveT opCast(T)() const if (is(T : Vec!(vecsize, S), S)) { T converted; static foreach (i; 0 .. vecsize) converted.content[i] = cast(S) content[i]; return converted; }When I try to use this, I get the error 'undefined identifier S'. Alternatively using:T opCast(T, S)() . . .causes the error 'template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()' I found using 'alias S = typeof(content[0])' works as a kind of ducttape alternative, however this seems like a codesmell to me, and I fear it won't scale well either. Am I missing a simple solution? And why is there no automatic argument deduction in this scenario when compared to normal function calls? (if the above opcast is translated to 'foo.opCast!T`)
Jan 10 2021
On Monday, 11 January 2021 at 00:48:49 UTC, Steven Schveighoffer wrote:I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)()Oh wouw, this seems to work perfectly! Awesome thanks ^^ Any Idea whyT opCast(T, S)() const if (is(T : Vec!(grootte, S))) {yields the errortemplate instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()while your suggestion does not? It seems to me it should match equally well. Also I had no clue types inferred in constraints were inaccessibly, I'll try to keep that in mind, though I wonder, is there is any specific reason for that? Then again since your example works inferred values shouldnt be necessary in constraints anyway. (On that note, is there per chance something like the underscore '_' as in python? In some cases I don't care for all the template arguments inside an is expression (with the (a:b, c) version))
Jan 10 2021
On Monday, 11 January 2021 at 03:40:41 UTC, Paul wrote:On Monday, 11 January 2021 at 00:48:49 UTC, Steven Schveighoffer wrote:The compiler does not look at template constraints until after it figures out what all the template arguments are. So, in your version, it sees T opCast(T, S) // ... opCast!(Vec!(2, double)) ...and is able to deduce that `T = Vec!(2, double)`, but doesn't have any way to figure out what `S` is, so it gives up.I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)()Oh wouw, this seems to work perfectly! Awesome thanks ^^ Any Idea whyT opCast(T, S)() const if (is(T : Vec!(grootte, S))) {yields the errortemplate instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()while your suggestion does not? It seems to me it should match equally well.
Jan 10 2021