www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Bypassing const with a union

reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
  While working on the BitArray I've come across an interesting 
dilemma, which is both bad and good. Let's take the following

struct S {
   size_t[] array;
}

  Simple enough, however make a const function, and suddenly you 
can't return a copy of it.

   S copy() const {
     return this; //compile-time error
   }

  Not bad, type system is doing it's job. Let's say I want to 
adjust the pointer as a slice

   S opSlice(int s, int e) { //non-const-able call
     S sl;
     sl.array = this.array[s .. e];
     return sl;
   }

  Good, but if I wanted to return an const item (as above) we have 
an issue. Since I want to change the pointer (but not it's 
contents) as a slice, the const system gets in the way without 
duping it.

   //from const, to const
   const(S) opSlice(int s, int e) const {
     S s = this;  //compile error this.array is const
     s.array = this[s .. e]; //compile error
     return s;
   }

   The item starts as const and ends as const and no data (other 
than maybe the struct which is new) changes; we should be able to 
adjust the pointer if we want. If I use a union I can bypass this.

struct S {
   union {
     size_t[] array;
     size_t[array.sizeof / size_t.sizeof] nonConst; //since it's a 
fixed array
   }

   const(S) opSlice(int s, int e) const {
     S sl;
     sl.nonConst = nonConst; //bypass const!
     sl.array = sl.array[s .. e];
     return sl;
   }
}

  Part of the simpler solutions go away when I try to const a 
struct when cast's have already been declared as with BitArray's 
original design. Meaning I couldn't just do this:

  return const(BitArray) s; //template not found for opCast(T) or 
something

  Would I use const within the cast?

   const(BitArray) opCast(T : BitArray)() { //or something similar?
     return const(BitArray) b; //infinite loop?
   }



  Although this means it could be quite dangerous, used in the 
right way it can also be a good thing. However consider this:

union X {
   string str;
   char[] chr;
}

char[] nonConstString(string inString){
   X x;
   x.str = inString;
   return x.chr; //immutable becomes non-immutable?
}

void bomb() {
   auto ncs = nonConstString("Go Boom!");
   ncs[0] = '!'; //should blow up or seg fault?
   writeln(ncs); //in my own test it silently succeeds
}

  Thinking about it, although this is possible you might still 
allow it but send warnings when the compiler detects it. Reason? 
Const takes effect too soon, sometimes before you can finish 
working on the changes. I've tried moving them to a new 
constructor(s) but keep having issues when the inputs are const 
as the struct assumes it's starting non-const. I think...

  Breaking the const system while your still building/preparing 
the new object should be allowed (as with the slice example) but 
once you pass it out it shouldn't be allowed anymore.
Jun 01 2012
next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Friday, 1 June 2012 at 20:24:39 UTC, Era Scarecrow wrote:
   //from const, to const
   const(S) opSlice(int s, int e) const {
     S s = this;  //compile error this.array is const
     s.array = this[s .. e]; //compile error
     return s;
   }

Correction: Seems this didn't get updated while I was testing this. //from const, to const const(S) opSlice(int s, int e) const { S sl = this; //compile error this.array is const sl.array = this[s .. e]; //compile error return sl; }
Jun 01 2012
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/01/2012 01:24 PM, Era Scarecrow wrote:

 Good, but if I wanted to return an const item (as above) we have an
 issue. Since I want to change the pointer (but not it's contents) as a
 slice, the const system gets in the way without duping it.

 //from const, to const
 const(S) opSlice(int s, int e) const {
 S s = this; //compile error this.array is const
 s.array = this[s .. e]; //compile error
 return s;
 }

Don't forget inout, which seems to be working at least in this case: import std.traits; import std.stdio; struct S { size_t[] array; void length(size_t length) property { array.length = length; } inout(S) copy() inout { return this; } inout(S) opSlice(int s, int e) inout { Unqual!S sl; sl.array = (cast(Unqual!S)this).array[s .. e]; return cast(inout(S))sl; } } void main() { auto s = S(); s.length = 10; const s3 = s[0..8]; const s4 = s3[1..3]; auto sc = s.copy(); auto s3c = s3.copy(); } std.traits.Unqual seems like a dirty trick there but I see it being used in Phobos as well.
 struct S {
 union {
 size_t[] array;
 size_t[array.sizeof / size_t.sizeof] nonConst; //since it's a fixed array
 }

I don't understand the calculation there. array.sizeof and size_t.sizeof are not related in that way. It works only if 'array' is fixed-length as well. Ali -- D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
Jun 01 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Friday, 1 June 2012 at 21:15:18 UTC, Ali Çehreli wrote:
 Don't forget inout, which seems to be working at least in this 
 case:

I'd given up on trying to use inout, being as it's confusing to try and use, when I try to use it it, it always complains. Perhaps better documentation/examples or I gotta look at it all over again. To my understanding you had to have inout in the signature and as a input parameter (which no input arguments never fit). Hmm..
 import std.traits;
 import std.stdio;

 struct S {
     size_t[] array;

     void length(size_t length)  property
     {
         array.length = length;
     }

     inout(S) copy() inout {
         return this;
     }

     inout(S) opSlice(int s, int e) inout {
         Unqual!S sl;
         sl.array = (cast(Unqual!S)this).array[s .. e];
         return cast(inout(S))sl;
     }
 }


 void main()
 {
     auto s = S();
     s.length = 10;

     const s3 = s[0..8];
     const s4 = s3[1..3];

     auto sc = s.copy();
     auto s3c = s3.copy();
 }

I'll try and use that, although Unqual is new to me. When going through the library some of it makes sense and a larger portion doesn't.
 std.traits.Unqual seems like a dirty trick there but I see it 
 being used in Phobos as well.

 struct S {
 union {
 size_t[] array;
 size_t[array.sizeof / size_t.sizeof] nonConst; //since it's a

 }

I don't understand the calculation there. array.sizeof and size_t.sizeof are not related in that way. It works only if 'array' is fixed-length as well.

The calculation is rather simple, but not so obvious. Convert to the size in bytes, then convert that size to how many words needed for the fixed array to fit. Had array been something a bit bigger, it would still work (assuming it's evenly divisible). Course you could always just include a magic number :P.
Jun 01 2012
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 02.06.2012 0:24, Era Scarecrow wrote:
 While working on the BitArray I've come across an interesting dilemma,
 which is both bad and good. Let's take the following

[snip]
 Thinking about it, although this is possible you might still allow it
 but send warnings when the compiler detects it. Reason? Const takes
 effect too soon, sometimes before you can finish working on the changes.
 I've tried moving them to a new constructor(s) but keep having issues
 when the inputs are const as the struct assumes it's starting non-const.
 I think...

There is also cast() that just cancels out all const/shared/immutable.
 Breaking the const system while your still building/preparing the new
 object should be allowed (as with the slice example)

Yes in constructor. Or by constructing incrementally a mutable object, inside pure function e.g. compiler can convert to immutable on return (auto-magically). but once you pass
 it out it shouldn't be allowed anymore.

-- Dmitry Olshansky
Jun 01 2012
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 02.06.2012 3:28, Era Scarecrow wrote:
 On Friday, 1 June 2012 at 23:14:14 UTC, Dmitry Olshansky wrote:
 There is also cast() that just cancels out all const/shared/immutable.

Only the first level, transitive const/immutable don't go away in my experience. Perhaps I'm doing it wrong, or perhaps it's just a protective feature to protect the lower levels so you don't get C++'s const system.
 Breaking the const system while your still building/preparing the new
 object should be allowed (as with the slice example)

Yes in constructor. Or by constructing incrementally a mutable object, inside pure function e.g. compiler can convert to immutable on return (auto-magically).

Which is sometimes where I'm getting stuck. In the constructor it complains about not convertible from const to mutable even if the object being passed back will be const/immutable.

Mmm IRC you can assign each field in const constructor only once. After that it's cooked and treated as const from now on.
 In my limited experience where it is emulating a slice I would need an
 exact copy of the struct and then modify what I need before passing it
 back; cast doesn't do the job, and manually copying const objects to
 non-const is an annoyance or a pain in it's own regard.

Sure it is. -- Dmitry Olshansky
Jun 02 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Friday, 1 June 2012 at 23:14:14 UTC, Dmitry Olshansky wrote:
 There is also cast() that just cancels out all 
 const/shared/immutable.

Only the first level, transitive const/immutable don't go away in my experience. Perhaps I'm doing it wrong, or perhaps it's just a protective feature to protect the lower levels so you don't get C++'s const system.
 Breaking the const system while your still building/preparing 
 the new object should be allowed (as with the slice example)

Yes in constructor. Or by constructing incrementally a mutable object, inside pure function e.g. compiler can convert to immutable on return (auto-magically).

Which is sometimes where I'm getting stuck. In the constructor it complains about not convertible from const to mutable even if the object being passed back will be const/immutable. In my limited experience where it is emulating a slice I would need an exact copy of the struct and then modify what I need before passing it back; cast doesn't do the job, and manually copying const objects to non-const is an annoyance or a pain in it's own regard.
Jun 01 2012
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 01 Jun 2012 18:51:54 -0400, Era Scarecrow <rtcvb32 yahoo.com>  =

wrote:

 On Friday, 1 June 2012 at 21:15:18 UTC, Ali =C3=87ehreli wrote:
 Don't forget inout, which seems to be working at least in this case:

I'd given up on trying to use inout, being as it's confusing to try =

 and use, when I try to use it it, it always complains. Perhaps better =

 documentation/examples or I gotta look at it all over again. To my  =

 understanding you had to have inout in the signature and as a input  =

 parameter (which no input arguments never fit). Hmm..

inout is not completely fleshed out yet. It's turning out to be quite a= = complex problem to solve, but it's getting there.
 import std.traits;
 import std.stdio;

 struct S {
     size_t[] array;

     void length(size_t length)  property
     {
         array.length =3D length;
     }

     inout(S) copy() inout {
         return this;
     }

     inout(S) opSlice(int s, int e) inout {
         Unqual!S sl;
         sl.array =3D (cast(Unqual!S)this).array[s .. e];
         return cast(inout(S))sl;
     }
 }


I think this will work for slicing: inout(S) opSlice(int s, int e) inout { return inout(S)(array[s..e]); } I use this quite a bit in dcollections. This seems more attractive to m= e = to any other solution, especially if casting is involved. -Steve
Jun 25 2012