Thread overview | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
August 24, 2010 TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
This is a shortened version of some operator overloading code from page 372 (although some code is from pages before it), sorry for the long post: module binary_ops; import std.stdio : writeln; import std.traits; import std.exception; unittest { auto foo = CheckedInt!(int)(5); auto bar = CheckedInt!(int)(5); foo = foo + bar; writeln(foo.Value); } void main() { } struct CheckedInt(N) if (isIntegral!N) { private N value; this(N value) { this.value = value; } @property auto Value() { return value; } // addition CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+") { auto result = value + rhs.value; enforce(rhs.value >= 0 ? result >= value : result < value); return result; } } I don't understand why he's trying to return result here (unless it was a mistake). Result is going to have the type of "private N value", whatever N may be, and this is conflicting with the return type which is CheckedInt. So, this won't compile. He has the same returns for subtraction and multiplication, but for others like division, shift and bitwise overload he has this: // division and remainder CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "/" || op == "%") { enforce(rhs.value != 0); return CheckedInt(mixin("value" ~ op ~ "rhs.value")); } This looks correct. If I change the add overload from the code to this: // addition CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+") { auto result = value + rhs.value; enforce(rhs.value >= 0 ? result >= value : result < value); return CheckedInt(mixin("value" ~ op ~ "rhs.value")); } Then the return statement calls a CheckedInt constructor and I get back a CheckedInt struct with the right value in the private variable "value". What I don't understand is how the constructor can be called like that. In my example the mixin would convert the code to: return CheckedInt(10); But if I ever tried a call like "CheckedInt(10)" in a unittest block, it wouldn't work. So how does this magic work? |
August 24, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Tue, 24 Aug 2010 17:19:25 -0500, Andrej Mitrovic <andrej.mitrovich@whatever.com> wrote: > [snip] > > struct CheckedInt(N) if (isIntegral!N) > { > private N value; > this(N value) > { > this.value = value; > } > @property > auto Value() > { > return value; > } > // addition > CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+") > { > auto result = value + rhs.value; > enforce(rhs.value >= 0 ? result >= value : result < value); > return result; > } > } > > I don't understand why he's trying to return result here (unless it was a mistake). Result is going to have the type of "private N value", whatever N may be, and this is conflicting with the return type which is CheckedInt. So, this won't compile. That's a bug. The return value should be CheckedInt(result); > > He has the same returns for subtraction and multiplication, but for others like division, shift and bitwise overload he has this: > > // division and remainder > CheckedInt opBinary(string op)(CheckedInt rhs) > if (op == "/" || op == "%") > { > enforce(rhs.value != 0); > return CheckedInt(mixin("value" ~ op ~ "rhs.value")); > } > > This looks correct. If I change the add overload from the code to this: > > // addition > CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+") > { > auto result = value + rhs.value; > enforce(rhs.value >= 0 ? result >= value : result < value); > return CheckedInt(mixin("value" ~ op ~ "rhs.value")); > } > > Then the return statement calls a CheckedInt constructor and I get back a CheckedInt struct with the right value in the private variable "value". > > What I don't understand is how the constructor can be called like that. In my example the mixin would convert the code to: > > return CheckedInt(10); > > But if I ever tried a call like "CheckedInt(10)" in a unittest block, it wouldn't work. So how does this magic work? > http://www.digitalmars.com/d/2.0/mixin.html -- Yao G. |
August 24, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Yao G. | Yao G. Wrote: > That's a bug. The return value should be CheckedInt(result); I'll add that to the errata. Yao G. Wrote: > > http://www.digitalmars.com/d/2.0/mixin.html I wasn't refering to the mixin, but the call to CheckedInt(). mixin compiles "value" ~ op ~ "rhs.value", which in this case evaluates to 5 + 5 and the whole call becomes CheckedInt(10). What I don't understand is how you can construct a new CheckedInt struct by calling it with CheckedInt(10), when I have to use a call like CheckedInt!(int)(10) outside the struct (in main or in a unittest block). |
August 24, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Tue, 24 Aug 2010 17:43:49 -0500, Andrej Mitrovic <andrej.mitrovich@whatever.com> wrote: > I wasn't refering to the mixin, but the call to CheckedInt(). mixin compiles "value" ~ op ~ "rhs.value", which in this case evaluates to 5 + 5 and the whole call becomes CheckedInt(10). Sorry. My mistake, I probably misread. > What I don't understand is how you can construct a new CheckedInt struct by calling it with CheckedInt(10), when I have to use a call like CheckedInt!(int)(10) outside the struct (in main or in a unittest block). This, I don't know. Maybe when you are inside the struct's body the compiler somehow can infer the type when CheckedInt is instantiated. Or maybe it does some kind of automatic alias (alias CheckedInt!10 CheckedInt). Outside of the struct's body, as there aren't template default values, is not possible to infer the type of CheckedInt. Maybe someone with more experience on templates can give you a better (or correct, I'm just guessing this :) ) answer. -- Yao G. |
August 24, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | Andrej Mitrovic wrote:
> Yao G. Wrote:
>
>> That's a bug. The return value should be CheckedInt(result);
>
> I'll add that to the errata.
>
>
> Yao G. Wrote:
>
>> http://www.digitalmars.com/d/2.0/mixin.html
>
> I wasn't refering to the mixin, but the call to CheckedInt(). mixin compiles "value" ~ op ~ "rhs.value", which in this case evaluates to 5 + 5 and the whole call becomes CheckedInt(10).
>
> What I don't understand is how you can construct a new CheckedInt struct by calling it with CheckedInt(10), when I have to use a call like CheckedInt!(int)(10) outside the struct (in main or in a unittest block).
It is the same in C++: the name of the template is equivalent to the current instantiation of the template.
foo() and bar() are both legal:
template <class T> class C
{
public:
C foo()
{
return C();
}
C<T> bar()
{
return C<T>();
}
};
int main()
{
C<int> c;
c.foo();
c.bar();
}
It seems to be the same in D. I don't know whether this is intended, or just a left over from the C++ parts of dmd. (I assume dmd shares code with the Digital Mars C++ compiler.)
Ali
|
August 25, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | Ali Çehreli: > It is the same in C++: the name of the template is equivalent to the > current instantiation of the template. > ... > It seems to be the same in D. I don't know whether this is intended, or > just a left over from the C++ parts of dmd. (I assume dmd shares code > with the Digital Mars C++ compiler.) I don't like this "feature" much, it has caused me troubles: http://d.puremagic.com/issues/show_bug.cgi?id=3950 Bye, bearophile |
August 25, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Tue, 24 Aug 2010 18:43:49 -0400, Andrej Mitrovic <andrej.mitrovich@whatever.com> wrote: > I wasn't refering to the mixin, but the call to CheckedInt(). mixin compiles "value" ~ op ~ "rhs.value", which in this case evaluates to 5 + 5 and the whole call becomes CheckedInt(10). > > What I don't understand is how you can construct a new CheckedInt struct by calling it with CheckedInt(10), when I have to use a call like CheckedInt!(int)(10) outside the struct (in main or in a unittest block). Inside a template instantiation, the template name without template parameters is equivalent to the current instantiation. It saves a lot of typing. -Steve |
August 25, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer:
> Inside a template instantiation, the template name without template parameters is equivalent to the current instantiation.
>
> It saves a lot of typing.
And causes some troubles :-) It's a barter, and I am not sure it's a good one.
Bye,
bearophile
|
August 25, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Ok I think I am kind of getting this. The template name inside a template is it's instantiation. I can do "CheckedInt.variable" and get back the value of "variable" in the current instantiation.
The trouble is, when you do a call like CheckedInt() you will loose all other data that you had before:
module binary_ops;
import std.stdio : writeln;
import std.traits;
import std.exception;
unittest
{
auto foo = CheckedInt!(int)(5);
auto bar = CheckedInt!(int)(5);
foo.x = 4;
bar.x = 5;
foo = foo + bar;
writeln(foo.x); // writes 0
writeln(bar.x); // writes 5
}
void main() { }
struct CheckedInt(N) if (isIntegral!N)
{
private N value;
int x;
this(N value)
{
this.value = value;
}
// addition
CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+")
{
auto result = value + rhs.value;
enforce(rhs.value >= 0 ? result >= value : result < value);
return CheckedInt(result);
}
}
Here I've lost the value of x. "return CheckedInt(result);" calls the constructor of the already instantiated template, but because of the way D works (afaik) it first has to deconstruct the object before constructing it again. And that includes initializing all members to their .init value before calling the constructor.
So, I don't like that return statement at all..
Steven Schveighoffer Wrote:
> On Tue, 24 Aug 2010 18:43:49 -0400, Andrej Mitrovic <andrej.mitrovich@whatever.com> wrote:
>
>
> > I wasn't refering to the mixin, but the call to CheckedInt(). mixin compiles "value" ~ op ~ "rhs.value", which in this case evaluates to 5 + 5 and the whole call becomes CheckedInt(10).
> >
> > What I don't understand is how you can construct a new CheckedInt struct by calling it with CheckedInt(10), when I have to use a call like CheckedInt!(int)(10) outside the struct (in main or in a unittest block).
>
> Inside a template instantiation, the template name without template parameters is equivalent to the current instantiation.
>
> It saves a lot of typing.
>
> -Steve
|
August 25, 2010 Re: TDPL: Operator Overloading | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Wed, 25 Aug 2010 09:55:47 -0400, bearophile <bearophileHUGS@lycos.com> wrote:
> Steven Schveighoffer:
>> Inside a template instantiation, the template name without template
>> parameters is equivalent to the current instantiation.
>>
>> It saves a lot of typing.
>
> And causes some troubles :-) It's a barter, and I am not sure it's a good one.
What trouble? I've already stated in the bug report how there's not anything better you can expect from the compiler. If it was required to specify the template parameters instead of using the shortcut, then you would be no better off. Here is something I had found to be a problem with dcollections:
final class TreeMap(K, V, alias ImplTemp=RBTree, alias compareFunc=DefaultCompare)
{
...
TreeMap!(K, V) set(K k, V v) {...}
...
}
Notice the problem?
-Steve
|
Copyright © 1999-2021 by the D Language Foundation