View mode: basic / threaded / horizontal-split · Log in · Help
December 07, 2012
Problem with const correctness
Consider this example:

struct Array(Type_)
{
public:
this(Type_ array_[]...)
{
_array = array_;
}
this(this)
{
_array = _array.dup;
}
ref Array!Type_ opAssign(in Array!Type_ array_)
{
_array = array_._array.dup;
return this;
}
private:
Type_[] _array;
}
unittest
{
Array!int one = Array!int(1, 2, 3, 4, 5);
immutable Array!int two = one; //Error: conversion error from Array!(int)
to immutable(Array!(int))
}
I enforce value-type semantics by duplicating the arrays on copy, so it
should behave like a value type with no indirections (implicitly convert to
immutable).
What do I need to do for this to work?

-- 
Bye,
Gor Gyolchanyan.
December 07, 2012
Re: Problem with const correctness
Gor Gyolchanyan:

> I enforce value-type semantics by duplicating the arrays on 
> copy, so it
> should behave like a value type with no indirections 
> (implicitly convert to
> immutable).
> What do I need to do for this to work?

This is a first try, not tested much, and I don't know how many
copies it performs:


import std.traits;

struct Array(T) {
     this(T items[]...) {
         this._array = items;
     }

     this(this) {
         static if (isMutable!T)
             this._array = this._array.dup;
         else
             this._array = this._array.idup;
     }

     ref Array opAssign(in Array other) {
         static if (isMutable!T)
             this._array = other._array.dup;
         else
             this._array = other._array.idup;
         return this;
     }

     @property Array!(immutable(T)) idup() {
         return typeof(return)(this._array.idup);
     }

     private T[] _array;
}

void main() {
     auto one = Array!int(1, 2, 3, 4, 5);
     immutable two = one.idup;
}


Bye,
bearophile
December 07, 2012
Re: Problem with const correctness
> struct Array(T) {
>      this(T items[]...) {
>          this._array = items;
>      }


For a D design bug, I think those items don't get copied. So be 
careful and test the code well.

Bye,
bearophile
December 07, 2012
Re: Problem with const correctness
On Fri, Dec 07, 2012 at 04:18:00PM +0100, bearophile wrote:
> >struct Array(T) {
> >     this(T items[]...) {
> >         this._array = items;
> >     }
> 
> 
> For a D design bug, I think those items don't get copied. So be
> careful and test the code well.
[...]

Yeah I ran into this issue before. Calling the above ctor by passing in
implicit array (e.g., like "auto x = Array!int(1,2,3);") passes a
*slice* of the function arguments _on the runtime stack_. Then the
assignment statement above simply copies the slice (*not* the data) to
this._array, which is most probably *not* what you want. Namely, once
the function that calls the ctor goes out of scope, your struct will be
holding a slice of invalid memory. (In fact, it doesn't even have to go
out of scope; if later code in the function uses up more runtime stack
space, it will overwrite the original array and thus invalidate the
slice.)

Workarounds:
- Use items.dup. Problem: if you're passing an actual array to the ctor,
 it's unnecessary and inefficient.

- Use an array literal: auto x = Array!int([1,2,3]);, which I believe
 should allocate the array on the heap, and so you're safe to just copy
 the slice. This defeats the purpose of the "items..." syntax, though.


T

-- 
Chance favours the prepared mind. -- Louis Pasteur
December 07, 2012
Re: Problem with const correctness
H. S. Teoh:

> Workarounds:
> - Use items.dup. Problem: if you're passing an actual array to 
> the ctor,
>   it's unnecessary and inefficient.
>
> - Use an array literal: auto x = Array!int([1,2,3]);, which I 
> believe
>   should allocate the array on the heap, and so you're safe to 
> just copy
>   the slice. This defeats the purpose of the "items..." syntax, 
> though.

I think the right solution is to fix the design+compiler, because 
it's safer.

(But how do you get the original GC-less behaviour if you need 
max performance and you know what you are doing?)

Bye,
bearophile
December 07, 2012
Re: Problem with const correctness
On Fri, Dec 07, 2012 at 05:42:18PM +0100, bearophile wrote:
> H. S. Teoh:
> 
> >Workarounds:
> >- Use items.dup. Problem: if you're passing an actual array to the
> >  ctor, it's unnecessary and inefficient.
> >
> >- Use an array literal: auto x = Array!int([1,2,3]);, which I believe
> >  should allocate the array on the heap, and so you're safe to just
> >  copy the slice. This defeats the purpose of the "items..." syntax,
> >  though.
> 
> I think the right solution is to fix the design+compiler, because
> it's safer.

Agreed.


> (But how do you get the original GC-less behaviour if you need max
> performance and you know what you are doing?)
[...]

I think there's already an issue open where the compiler should warn you
of escaping reference to stack arguments. So the variadic arguments
syntax will work for the GC-less case, but when you need a GC you have
to explicitly make a heap array, e.g. using an array literal.

The problem comes because there is an ambiguity between runtime stack
slices and heap slices, which are conflated under a single syntax. When
there is no escaping reference, it doesn't matter which two they are,
but when there is, there's a problem.


T

-- 
Microsoft is to operating systems & security ... what McDonalds is to gourmet cooking.
December 07, 2012
Re: Problem with const correctness
On 12/07/2012 08:42 AM, bearophile wrote:
> H. S. Teoh:
>
>> Workarounds:
>> - Use items.dup. Problem: if you're passing an actual array to the ctor,
>> it's unnecessary and inefficient.
>>
>> - Use an array literal: auto x = Array!int([1,2,3]);, which I believe
>> should allocate the array on the heap, and so you're safe to just copy
>> the slice. This defeats the purpose of the "items..." syntax, though.
>
> I think the right solution is to fix the design+compiler, because it's
> safer.
>
> (But how do you get the original GC-less behaviour if you need max
> performance and you know what you are doing?)

I've run into the same issue before and have decided that I should just 
forget about T[]...-style parameters and just require an actual array:

    this(T items[])

That is both safe and efficient. If it is really cumbersome, a 
single-item overload can be provided as well:

    this(T item) {
        this([ item ]);    // untested but should work :p
    }

Ali
December 07, 2012
Re: Problem with const correctness
On 12/07/2012 06:18 AM, Gor Gyolchanyan wrote:

>   this(this)
> {
> _array = _array.dup;
> }
>   ref Array!Type_ opAssign(in Array!Type_ array_)
> {
> _array = array_._array.dup;
> return this;
> }

There has been discussions on the D.learn forum recently. Apparently, 
the compiler-generated opAssign calls the postblit automatically anyway. 
(Maybe also for user-defined opAssign as well. I am not sure.)

Unless there is a compiler bug about that, you may not need the opAssign 
definition after all.

Ali
December 08, 2012
Re: Problem with const correctness
On Friday, 7 December 2012 at 14:18:50 UTC, Gor Gyolchanyan wrote:
> Consider this example:
> I enforce value-type semantics by duplicating the arrays on 
> copy, so it
> should behave like a value type with no indirections 
> (implicitly convert to
> immutable).
> What do I need to do for this to work?

My approach is to have a general dup function. I call it gdup, 
for global dup so the name does not conflict with the existing 
dup. It dup's fields recursively. Feel free to have a look and 
any suggestions appreciated. Would greatly appreciate if seasoned 
D developers like (Ali,  bearophile, ...) would review - as I use 
these mixins to simplify development with structs.

https://github.com/patefacio/d-help/tree/master/d-help/opmix
docs at 
https://github.com/patefacio/d-help/blob/master/doc/canonical.pdf

Since the gdup makes all new deep copies (as dup should) the cast 
to immutable is safe. For reference structs with aliasing you 
have to worry about assignment of immutable to mutable and the 
reverse. Due to transitive immutability, both are disallowed 
without something special. dup, idup and gdup in general serve 
that purpose.

As Ali points out, I also don't think you need the opAssign at 
all in this case - since default opAssign calls postblit (which 
is nice). To get value semantics just implement postblit. If you 
add fields you should not need to worry about it.

As others pointed out - you want deep copy on the items in the 
array for your custom ctor. Since gdup is recursive and does not 
require the composed structs to have a postblit - it works even 
on arrays of T where T has aliasing but no postblit.

Thanks,
Dan

import std.stdio;
import std.traits;
import opmix.mix;

struct Array(Type_)
{
public:
  mixin(PostBlit);
  this(Type_[] array...) {
    _array = array.gdup;
  }
private:
  Type_[] _array;
}

unittest
{
  {
    Array!int one = Array!int(1, 2, 3, 4, 5);
    immutable Array!int two = cast(immutable)one.gdup;
    assert(two._array.ptr != one._array.ptr);
    assert(two._array == one._array);
  }

  {
    struct S {
      // look ma' no postblit
      char[] c;
    }
    Array!S one = Array!S(S(['d']), S(['o','g']), 
S(['b','o','y']));
    auto immutable two = cast(immutable)one.gdup;

    // Ensure the copy was deep (i.e. no sharing)
    
assert(cast(DeepUnqual!(typeof(two._array.ptr)))two._array.ptr !=
           one._array.ptr);
    assert(typesDeepEqual(two, one));
  }

}
December 10, 2012
Re: Problem with const correctness
On Saturday, 8 December 2012 at 21:47:32 UTC, Dan wrote:
> My approach is to have a general dup function. I call it gdup, 
> for global dup so the name does not conflict with the existing 
> dup. It dup's fields recursively. Feel free to have a look and 
> any suggestions appreciated. Would greatly appreciate if 
> seasoned D developers like (Ali,  bearophile, ...) would review 
> - as I use these mixins to simplify development with structs.

What would happen to the recursive dup if the structure contains 
a cycle (e.g. A has a reference to B, which has a reference to C, 
which has a reference to the original A)?
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home