August 25, 2010
On Wed, 25 Aug 2010 10:01:31 -0400, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> 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.

A struct is a value type, so you are making a copy regardless.   Your expectation that foo = ... does not wholly replace foo is incorrect.

You could do this:

this(N value, int x)
{
   this.value = value;
   this.x = x;
}

...

return CheckedInt(result, x);

...

and then you're x comes through.

Value types are always passed by value unless you use pointers or ref.

BTW, this has nothing to do with CheckedInt being a shortcut for CheckedInt!N inside the instantiated template.

-Steve
August 25, 2010
That wasn't my expectation, but nevermind. The example is ok in this case, it's just that care needs to be taken when making operator overloads. So maybe I overreacted a little. :)

Steven Schveighoffer Wrote:

> A struct is a value type, so you are making a copy regardless.   Your expectation that foo = ... does not wholly replace foo is incorrect.

August 25, 2010
On Tue, 24 Aug 2010 18:19:25 -0400, Andrej Mitrovic <andrej.mitrovich@whatever.com> wrote:

> 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?

BTW, a unit test model I use in dcollections quite a bit is putting unit tests inside the template itself.  Then the unit test enjoys the same benefits.  e.g.:

struct CheckedInt(N) if(isIntegral!N)
{
   void foo(N n)
   {
     ...
   }

   unittest
   {
      CheckedInt ci;
      ci.foo(1);
   }
}

The only caveat is you have to instantiate the template to get the unit test to run :)  So on the bottom of your module you have to do this:

unittest
{
    CheckedInt!int ci1;
    CheckedInt!uint ci2;
    ...
}

The great benefit however is if you write your unit tests in a generic way, you can rigorously test your struct with all possible template instantiations without writing any extra code.  I've found the only limit here is generating data -- the literals have to match the N type.  CheckedInt only instantiates if N is integral, but if that constraint wasn't there, then CheckedInt!string wouldn't compile because the unit test can't convert the literal 1 to a string.

-Steve
August 25, 2010
Interesting. :)

The following seems to work, although I don't know if that's a good idea?:

struct CheckedInt(N) // if(isIntegral!N)
{
    void foo(N n)
    {

    }

    unittest
    {
        CheckedInt ci;
        ci.foo(N.init);
    }
}

unittest
{
    CheckedInt!int ci1;
    CheckedInt!uint ci2;
    CheckedInt!string ci3;
}

void main() { }

Steven Schveighoffer Wrote:

>  I've found the only limit
> here is generating data -- the literals have to match the N type.
> CheckedInt only instantiates if N is integral, but if that constraint
> wasn't there, then CheckedInt!string wouldn't compile because the unit
> test can't convert the literal 1 to a string.
> 
> -Steve

August 25, 2010
Although that might not work with floats since that would call foo with a NaN value, and if it used that, well that wouldn't really work.

I could use N.max, but that doesn't work with strings.

Andrej Mitrovic Wrote:

> The following seems to work, although I don't know if that's a good idea?:

August 25, 2010
What would be really cool is if we had a property that returned a random value of any integrated type. And for user-defined types, maybe it would call a method with a special name. I guess one could make a template function that would do just that.

Andrej Mitrovic Wrote:

> Although that might not work with floats since that would call foo with a NaN value, and if it used that, well that wouldn't really work.
> 
> I could use N.max, but that doesn't work with strings.
> 
> Andrej Mitrovic Wrote:
> 
> > The following seems to work, although I don't know if that's a good idea?:
> 

August 26, 2010
On Wed, Aug 25, 2010 at 17:27, Andrej Mitrovic <andrej.mitrovich@test.com>wrote:

> What would be really cool is if we had a property that returned a random value of any integrated type. And for user-defined types, maybe it would call a method with a special name. I guess one could make a template function that would do just that.
>
>
It reminds me a bit of Haskell's QuickCheck library, used to test code. http://hackage.haskell.org/package/QuickCheck-2.1.1.1

There is an Arbitrary typeclass that the user defines to generate an
arbitrary value for a given type.
With a D template, it's easy to do that for all numeric types (or, any
'range' type, like char) and arrays / associative arrays. This way,
generating it for classes and struct is easy. The real difficulty would be
to generate an arbitrary function from int function(int), for example.

And, to test, you don't want a perfectly random value: you need the extrema, degenerate cases like NaN, null pointers, empty arrays, etc.

Philippe


1 2
Next ›   Last »