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.