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