Jump to page: 1 2
Thread overview
TDPL: Operator Overloading
Aug 24, 2010
Andrej Mitrovic
Aug 24, 2010
Yao G.
Aug 24, 2010
Andrej Mitrovic
Aug 24, 2010
Yao G.
Aug 24, 2010
Ali Çehreli
Aug 25, 2010
bearophile
Aug 25, 2010
bearophile
Aug 25, 2010
Andrej Mitrovic
Aug 25, 2010
Andrej Mitrovic
Aug 25, 2010
Andrej Mitrovic
Aug 25, 2010
Andrej Mitrovic
Aug 25, 2010
Andrej Mitrovic
Aug 26, 2010
Philippe Sigaud
August 24, 2010
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
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
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
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
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
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
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
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
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
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
« First   ‹ Prev
1 2