struct / cast / ? design problem

Charles Hixson via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Mar 16 12:49:40 PDT 2015


On 03/16/2015 11:55 AM, ketmar via Digitalmars-d-learn wrote:
> On Mon, 16 Mar 2015 11:18:16 -0700, Charles Hixson via Digitalmars-d-learn
> wrote:
>
>> My current best answer is to turn the "unused" into a vector of bytes,
>> and then pass that to the using routines as a ref.  This still is wide
>> open to errors at the calling end, but the BlockHead end can ensure that
>> the length of the vector remains constant whenever it accesses it (well,
>> whenever the head is being written to disk.  The byte vector would be a
>> static array at the BlockHead end.  This would seem to allow the using
>> end to cast the byte vector into an appropriate struct for its own use,
>> and that writes to it would be seen by BlockHead as writes to the
>> header...but only to restricted parts of the header.  The problem here
>> is that the ref array might allow itself to be resized at the user end.
>> That's not a problem as long as the resizing is to something smaller,
>> but I'm not sure what happens if it gets resized to something larger. It
>> looks like allowing a buffer overflow.
> if you passing the array, not a slice, it can't be resized. i.e.:
>
>    void foo (ref ubyte[8] a) {
>      a[2] = 42;
>      //a.length = 6; // this will not compile: constant a.length is not an
> lvalue
>    }
>
>    void main () {
>      ubyte[8] aa;
>      assert(aa[2] == 0);
>      foo(aa);
>      assert(aa[2] == 42);
>    }
Yes, but if I pass an array, the using program can't change it's own 
values (which need to be stored in the header).  Additionally, if I pass 
an array I'm passing over 400 bytes rather than around 16, but that's 
very secondary.
So, attempting to rewrite your example into more what I am talking about:
import    std.stdio;

struct    tstHead
{
     ubyte    data[400];

     ref    ubyte[400]    value    ()
     {    return    data;    }

     void    write(int i)
     {    writefln ("%d", data[i]);    }
}

void    main()
{    tstHead    tst;
     auto    val    =    tst.value;
     val[23]    =    23;
     writefln("%d", val[23]);
     tst.write(23);
     tst.write(0);
}
Yields:
23
0
0
instead of:
23
23
0

And:import    std.stdio;

struct    tstHead
{
     ubyte    data[400];

     void    value    (ref byte[400] val)
     {    val.ptr    =    data;    }

     void    write(int i)
     {    writefln ("%d", data[i]);    }
}

void    main()
{    tstHead    tst;
     ubyte    val[400];
     tst.value (val);
     val[23]    =    23;
     writefln("%d", val[23]);
     tst.write(23);
     tst.write(0);
}
Yields:
test.d(8): Error: val.ptr is not an lvalue
test.d(17): Error: function test.tstHead.value (ref byte[400] val) is 
not callable using argument types (ubyte[400])
Failed: ["dmd", "-unittest", "-Dddocs", "-v", "-o-", "test.d", "-I."]

It is *necessary* that the using routine be able to store its data into 
the parts of the header reserved for it.  Perhaps multiple copying will 
be necessary.  I could write getter and setter routines, but they will 
inevitably be doing a lot of excess copying as the base routine doesn't 
know what data the calling routine will need.  Every solution I've 
thought of either requires a lot of excess copying, or requires a very 
tight coupling between the "library" routine and the using routine.


More information about the Digitalmars-d-learn mailing list