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.

Reply via email to