digitalmars.D - Operators overloading
- matovitch (65/65) May 19 2013 Hello from France everyone ! (I hope my english will be
- Jonathan M Davis (38/112) May 19 2013 If you want to distinguish between overloads of templated functions base...
- matovitch (8/10) May 19 2013 I understand but it isn't about something which is inside the
- bearophile (5/19) May 19 2013 It's named std.algorithm.canFind:
- Jonathan M Davis (6/29) May 19 2013 Ah, yes. I was thinking backwards. At first, I was thinking canFind(op, ...
- Timon Gehr (19/42) May 19 2013 It is not a valid transformation. This removes the condition from the
- matovitch (3/50) May 19 2013 Oh yes ! You are right...it takes a vertex in both cases. Thanks
- Idan Arye (30/51) May 19 2013 I think a better syntax would be:
- Jonathan M Davis (8/11) May 19 2013 Actually, it's usually considered good practice to use the same function...
- Timon Gehr (7/36) May 19 2013 Your example shows how operator overloading code often fits for multiple...
- matovitch (2/5) May 19 2013 I tried this first... Yes, it's a better syntax.
Hello from France everyone ! (I hope my english will be readable...) Yesterday, I discover D and that was really really great. I am far from being a guru (it seems there are a lot here ;-) but I foud the language clear and expressive the documentation is very well done futhermore dmd is incredibly fast and the logs are pure awesomness (a gcc command often doesn't fit on my shell). I know you are waiting for a 'but' and there is one : it's about operators overloading. First, but that kind of ok with me, I don't like the syntax : const Vertex opBinary(string op)(const ref Vertex vertex) if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } I don't like the 'if' outside and the condition could be really long : if (op == "+" || op == "-" || op == "*" || op == "/"). Okay, putting the if inside is quite easy : const Vertex opBinary(string op)(const ref Vertex vertex) { static if if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } But...I'd like to define cross product : const T opBinary(string op)(const ref Vertex vertex) { static if (op == "*") { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } } The compiler is not so great here and doesn't distinguish the two opBinary by their signatures. Why ? Before looking at the documentation again I tried something like that : const Vertex opBinary(string op : ["+", "-"])(const ref Vertex vertex) { { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } const T opBinary(string op : "*")(const ref Vertex vertex) { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } The second one is okay, but the fisrt one isn't correct. I think this kind of template specialization over a list could be great with mixins and static if/switch. What do you think ? May be there is an other way to do what I want...
May 19 2013
On Sunday, May 19, 2013 09:32:09 matovitch wrote:Hello from France everyone ! (I hope my english will be readable...) Yesterday, I discover D and that was really really great. I am far from being a guru (it seems there are a lot here ;-) but I foud the language clear and expressive the documentation is very well done futhermore dmd is incredibly fast and the logs are pure awesomness (a gcc command often doesn't fit on my shell). I know you are waiting for a 'but' and there is one : it's about operators overloading. First, but that kind of ok with me, I don't like the syntax : const Vertex opBinary(string op)(const ref Vertex vertex) if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } I don't like the 'if' outside and the condition could be really long : if (op == "+" || op == "-" || op == "*" || op == "/"). Okay, putting the if inside is quite easy : const Vertex opBinary(string op)(const ref Vertex vertex) { static if if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } But...I'd like to define cross product : const T opBinary(string op)(const ref Vertex vertex) { static if (op == "*") { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } } The compiler is not so great here and doesn't distinguish the two opBinary by their signatures. Why ? Before looking at the documentation again I tried something like that : const Vertex opBinary(string op : ["+", "-"])(const ref Vertex vertex) { { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } const T opBinary(string op : "*")(const ref Vertex vertex) { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } The second one is okay, but the fisrt one isn't correct. I think this kind of template specialization over a list could be great with mixins and static if/switch. What do you think ? May be there is an other way to do what I want...If you want to distinguish between overloads of templated functions based on template parameters, template constraints are the correct way to do it. Nothing inside of the function - including static ifs - is considered in overloading. By the time the compiler even looks at that code, the overload has already been decided (or determined to be ambiguous). And what you can do directly in the template parameters to distinguish them - e.g. string op : "+" - is extremely limited. Much as you may not like it, the correct way to handle this is with template constraints. That's what they're for. Now, there are ways to reduce their length - particularly if you're testing the same thing often. In that case, you could make an eponymous template for the test. template isAddOrSub(string op) { enum isAddOrSub = op == "+" || op == "-"; } Then you can do const Vertex opBinary(string op)(const ref Vertex vertex) if (isAddOrSub!op) {...} Now, in this particular case, I wouldn't think that it would be worth it. Which operators you need to use with a particular overload varies too much from type to type for an eponymous template like that to make much sense. Another alternative would be to use a function like std.algorithm.equal except that it tested that one of the arguments was equal to the first one rather than just testing one argument against it. Something like const Vertex opBinary(string op)(const ref Vertex vertex) if (isOneOf(op, "+", "-")) {...} Off the top of my head, I'm not aware of a function like that in the standard library, but I may just not being think of it, and it probably wouldn't be all that hard to write, at least if efficiency wasn't a major concern. In general though, AFAIK, most people just do const Vertex opBinary(string op)(const ref Vertex vertex) if (op == "+" || op == "-") {...} and don't have a problem with it. - Jonathan M Davis
May 19 2013
Fisrt, thank you for your clear answer. On Sunday, 19 May 2013 at 09:04:23 UTC, Jonathan M Davis wrote:Nothing inside of the function - including static ifs - is considered in overloading.I understand but it isn't about something which is inside the function : it is about its signature. I agree that sometimes it might be useful to use more than a list of or, so the idea of template specilization over a list is stupid and theses template constraints are useful (even if I don't like the syntax, I couldn't imagine a better one).
May 19 2013
Jonathan M Davis:Another alternative would be to use a function like std.algorithm.equal except that it tested that one of the arguments was equal to the first one rather than just testing one argument against it. Something like const Vertex opBinary(string op)(const ref Vertex vertex) if (isOneOf(op, "+", "-")) {...} Off the top of my head, I'm not aware of a function like that in the standard library, but I may just not being think of it, and it probably wouldn't be all that hard to write, at least if efficiency wasn't a major concern.It's named std.algorithm.canFind: if (["+", "-"].canFind(op)) Bye, bearophile
May 19 2013
On Sunday, May 19, 2013 12:36:00 bearophile wrote:Jonathan M Davis:Ah, yes. I was thinking backwards. At first, I was thinking canFind(op, "+", "-"), which definitely doesn't do the right thing, and for some reason, it didn't occur to me to flip it. I've probably been up too long and should get to bed. - Jonathan M DavisAnother alternative would be to use a function like std.algorithm.equal except that it tested that one of the arguments was equal to the first one rather than just testing one argument against it. Something like const Vertex opBinary(string op)(const ref Vertex vertex) if (isOneOf(op, "+", "-")) {...} Off the top of my head, I'm not aware of a function like that in the standard library, but I may just not being think of it, and it probably wouldn't be all that hard to write, at least if efficiency wasn't a major concern.It's named std.algorithm.canFind: if (["+", "-"].canFind(op))
May 19 2013
On 05/19/2013 09:32 AM, matovitch wrote:Okay, putting the if inside is quite easy :It is not a valid transformation. This removes the condition from the signature.const Vertex opBinary(string op)(const ref Vertex vertex) { static /*[...]*/ if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } But...I'd like to define cross product : const T opBinary(string op)(const ref Vertex vertex) { static if (op == "*") { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } } The compiler is not so great here and doesn't distinguish the two opBinary by their signatures. Why ?Because the two signatures are identical. const T opBinary(string op)(const ref Vertex vertex); Do this: const Vertex opBinary(string op)(const ref Vertex vertex) if (op == "+" || op == "-"){ Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } const T opBinary(string op)(const ref Vertex vertex) if (op == "*"){ T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; }
May 19 2013
On Sunday, 19 May 2013 at 10:03:27 UTC, Timon Gehr wrote:On 05/19/2013 09:32 AM, matovitch wrote:Oh yes ! You are right...it takes a vertex in both cases. Thanks for losing your time with me. :-)Okay, putting the if inside is quite easy :It is not a valid transformation. This removes the condition from the signature.const Vertex opBinary(string op)(const ref Vertex vertex) { static /*[...]*/ if (op == "+" || op == "-") { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } But...I'd like to define cross product : const T opBinary(string op)(const ref Vertex vertex) { static if (op == "*") { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } } The compiler is not so great here and doesn't distinguish the two opBinary by their signatures. Why ?Because the two signatures are identical. const T opBinary(string op)(const ref Vertex vertex); Do this: const Vertex opBinary(string op)(const ref Vertex vertex) if (op == "+" || op == "-"){ Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } const T opBinary(string op)(const ref Vertex vertex) if (op == "*"){ T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; }
May 19 2013
On Sunday, 19 May 2013 at 07:32:11 UTC, matovitch wrote:const Vertex opBinary(string op : ["+", "-"])(const ref Vertex vertex) { { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; } } const T opBinary(string op : "*")(const ref Vertex vertex) { T result = 0; foreach(i; 0..vertex_size) result += coordinate[i] * vertex.coordinate[i]; return result; } The second one is okay, but the fisrt one isn't correct. I think this kind of template specialization over a list could be great with mixins and static if/switch. What do you think ? May be there is an other way to do what I want...I think a better syntax would be: const Vertex opBinary(string op : "+", "-")(const ref Vertex vertex) this will save a bit of ambiguity(`op` is a `string` specialization of `string[]`?) At any rate, I'm not sure how useful it would be. Operator overloading code usually fit for a single operator - you usually write one for each operator: const Vertex opBinary(string op : "+")(const ref Vertex vertex) { Vertex result = this; result += vertex; return result; } const Vertex opBinary(string op : "-")(const ref Vertex vertex) { Vertex result = this; result -= vertex; return result; } If you want to have several operators in the same method, you can afford the verbosity of: const Vertex opBinary(string op)(const ref Vertex vertex) if (__traits(compiles, mixin("this " ~ op ~ "= vertex;"))) { Vertex result = *this; mixin("result" ~ op ~ "= vertex;"); return result; }
May 19 2013
On Sunday, May 19, 2013 12:41:52 Idan Arye wrote:At any rate, I'm not sure how useful it would be. Operator overloading code usually fit for a single operator - you usually write one for each operator:Actually, it's usually considered good practice to use the same function for several operators and mixin the operator. In most cases, with arithmetic operators, that allows you to use one function for several operators and is the main reason why overloaded operators now use strings like they do rather than being opAdd, opSub, etc. like they used to be. Sometimes, it _is_ necessary to have an overload per operator, but usually that can be avoided. - Jonathan M Davis
May 19 2013
On 05/19/2013 12:41 PM, Idan Arye wrote:... I think a better syntax would be: const Vertex opBinary(string op : "+", "-")(const ref Vertex vertex) this will save a bit of ambiguity(`op` is a `string` specialization of `string[]`?)It moves the ambiguity from analysis into the parser.At any rate, I'm not sure how useful it would be. Operator overloading code usually fit for a single operator - you usually write one for each operator: const Vertex opBinary(string op : "+")(const ref Vertex vertex) { Vertex result = this; result += vertex; return result; } const Vertex opBinary(string op : "-")(const ref Vertex vertex) { Vertex result = this; result -= vertex; return result; }Your example shows how operator overloading code often fits for multiple operators.If you want to have several operators in the same method, you can afford the verbosity of: const Vertex opBinary(string op)(const ref Vertex vertex) if (__traits(compiles, mixin("this " ~ op ~ "= vertex;"))) { Vertex result = /*[...]*/this; mixin("result" ~ op ~ "= vertex;"); return result; }This kind of template constraint may allow leaking of implementation details. Eg. v.opBinary!".foo+"
May 19 2013
On Sunday, 19 May 2013 at 10:41:53 UTC, Idan Arye wrote:I think a better syntax would be: const Vertex opBinary(string op : "+", "-")(const ref Vertex vertex)I tried this first... Yes, it's a better syntax.
May 19 2013