Re: struct / cast / ? design problem

2015-03-16 Thread via Digitalmars-d-learn
The problem in your example is that your making a copy of the 
returned data. Of course any changes to that copy won't affect 
the original. You need to return a pointer to it (`ref` won't do 
if you want to store it in a local variable, because these can't 
be `ref`).


struct BlockHead
{
uint magic = 20150312;
uint magic2;
uint blockSize;
uint unused1;
ulong unused2;
ulong spare[61];

T* embedded(T)() {
static assert(T.sizeof  spare.length);
return cast(T*) spare.ptr;
}
}

How to use it:

void useIt(ref BlockHead bh) {
static struct InternalData {
int foo;
ubyte[20] bar;
}

auto data = bh.embedded!InternalData;
data.foo = 42;
}


Re: struct / cast / ? design problem

2015-03-16 Thread Charles Hixson via Digitalmars-d-learn


On 03/15/2015 04:51 PM, ketmar via Digitalmars-d-learn wrote:

On Sun, 15 Mar 2015 16:34:14 -0700, Charles Hixson via Digitalmars-d-learn
wrote:

if you know the exact layouts of `spare`, you can use union for that:

struct S {
   // ...
   union {
 ulong[61] spare;
 struct { int vala; ubyte valb; }
 // etc.
   }
}

and then you can use it like this:

   auto s = S();
   s.vala = 42;
   assert(s.spare[0] == 42);

you can also write a CTFE function that builds struct to cast where
`spare` is changed to something else, using `S` as a source, but this
solution will not be pretty. ;-)
The original method had no knowledge of the layout that the using module 
will need, merely that it will need to store some information.  (In this 
particular case I *could* just modify the header in the original file, 
but that isn't solving the design problem, and means that working code 
needs to be modified to accommodate new code in a different file.)


IOW, if I could write the union, I wouldn't have needed to label the 
unused header memory spare.  That solution *would* allow for multiple 
secondary users, with different union values, but it would again mean 
that new code would require the modifying of the file containing 
existing code.  This is normally considered bad practice.




Re: struct / cast / ? design problem

2015-03-16 Thread Charles Hixson via Digitalmars-d-learn


On 03/16/2015 09:16 AM, Charles Hixson via Digitalmars-d-learn wrote:


On 03/15/2015 04:51 PM, ketmar via Digitalmars-d-learn wrote:
On Sun, 15 Mar 2015 16:34:14 -0700, Charles Hixson via 
Digitalmars-d-learn

wrote:

if you know the exact layouts of `spare`, you can use union for that:

struct S {
   // ...
   union {
 ulong[61] spare;
 struct { int vala; ubyte valb; }
 // etc.
   }
}

and then you can use it like this:

   auto s = S();
   s.vala = 42;
   assert(s.spare[0] == 42);

you can also write a CTFE function that builds struct to cast where
`spare` is changed to something else, using `S` as a source, but this
solution will not be pretty. ;-)
The original method had no knowledge of the layout that the using 
module will need, merely that it will need to store some information.  
(In this particular case I *could* just modify the header in the 
original file, but that isn't solving the design problem, and means 
that working code needs to be modified to accommodate new code in a 
different file.)


IOW, if I could write the union, I wouldn't have needed to label the 
unused header memory spare.  That solution *would* allow for 
multiple secondary users, with different union values, but it would 
again mean that new code would require the modifying of the file 
containing existing code.  This is normally considered bad practice.



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.


Re: struct / cast / ? design problem

2015-03-16 Thread ketmar via Digitalmars-d-learn
On Mon, 16 Mar 2015 12:49:40 -0700, Charles Hixson via Digitalmars-d-learn
wrote:

yep, you're doing it wrong, as Marc already wrote. ;-)

it's not very obvious, but this line makes a copy:

  auto val = tst.value;

there are no `ref` type in D. `ref` is modifier for function arguments, 
but not part of the type, and you can't declare `ref int`, for example. 
so that line transforms to this:

  ubyte[400] val = tst.value;

which, obviously, does copying. yet this will work as expected:

  import iv.writer;

  struct tstHead {
ubyte[400] data;

ubyte[] value () { return data; }
void write (int i) { writeln(data[i]); }
  }


  void test (ref ubyte[400] a) {
a[2] = 42;
  }


  void main () {
tstHead tst;
auto val = tst.value;
val[23] = 23;
writeln(val[23]);
tst.write(23);
tst.write(0);
test(val[0..400]);
tst.write(2);
  }



signature.asc
Description: PGP signature


Re: struct / cast / ? design problem

2015-03-16 Thread Charles Hixson via Digitalmars-d-learn


On 03/16/2015 01:24 PM, via Digitalmars-d-learn wrote:
The problem in your example is that your making a copy of the returned 
data. Of course any changes to that copy won't affect the original. 
You need to return a pointer to it (`ref` won't do if you want to 
store it in a local variable, because these can't be `ref`).


struct BlockHead
{
uint magic = 20150312;
uint magic2;
uint blockSize;
uint unused1;
ulong unused2;
ulong spare[61];

T* embedded(T)() {
static assert(T.sizeof  spare.length);
return cast(T*) spare.ptr;
}
}

How to use it:

void useIt(ref BlockHead bh) {
static struct InternalData {
int foo;
ubyte[20] bar;
}

auto data = bh.embedded!InternalData;
data.foo = 42;
}


Nice!  That looks like exactly what I was hoping for.


Re: struct / cast / ? design problem

2015-03-16 Thread ketmar via Digitalmars-d-learn
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);
  }


signature.asc
Description: PGP signature


Re: struct / cast / ? design problem

2015-03-16 Thread Charles Hixson via Digitalmars-d-learn


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:
importstd.stdio;

structtstHead
{
ubytedata[400];

refubyte[400]value()
{returndata;}

voidwrite(int i)
{writefln (%d, data[i]);}
}

voidmain()
{tstHeadtst;
autoval=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:importstd.stdio;

structtstHead
{
ubytedata[400];

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

voidwrite(int i)
{writefln (%d, data[i]);}
}

voidmain()
{tstHeadtst;
ubyteval[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.


Re: struct / cast / ? design problem

2015-03-15 Thread ketmar via Digitalmars-d-learn
On Sun, 15 Mar 2015 16:34:14 -0700, Charles Hixson via Digitalmars-d-learn
wrote:

if you know the exact layouts of `spare`, you can use union for that:

struct S {
  // ...
  union {
ulong[61] spare;
struct { int vala; ubyte valb; }
// etc.
  }
}

and then you can use it like this:

  auto s = S();
  s.vala = 42;
  assert(s.spare[0] == 42);

you can also write a CTFE function that builds struct to cast where 
`spare` is changed to something else, using `S` as a source, but this 
solution will not be pretty. ;-)

signature.asc
Description: PGP signature


struct / cast / ? design problem

2015-03-15 Thread Charles Hixson via Digitalmars-d-learn

I've got, say, a file header in a routine that looks like:
structBlockHead
{  uintmagic=20150312;//block magic
uintmagic2;//magic for use by 
the using routine

uintblockSize;
uintunused1;
ulongunused2;
ulongspare[61];
}

The unused vars are intended to be reserved for future use.  The 
spare vars are intended for use by another routine that uses the 
routines of the owner of BlockHead to deal with basic things.  So it 
needs to define the data in spare.


How?
The only almost nice way I've thought of is with a struct pointer to 
spare, and calling that nice is a statement about how bad the other ways 
I've thought of are.  (E.g., redefining spare as a ubyte vector and then 
casting the struct of the using program as a ubyte vector and copying it 
over.)


I'm sure that there must be a better way, but I can't think of what it 
is.  BlockHead needs to be a struct to allow the data to be written with 
a writeBlock.  I don't want to use a buffered file because this needs 
random access.  Perhaps I just shouldn't define spare, but then I 
wouldn't retrieve the data with the readBlock.  So I'd double the number 
of disk accesses (to the BlockHead).


One alternative is to have each using routine define it's own header 
struct, and to cast each BlockHead to
the appropriate routine's own header type, but that begs for errors to 
creep in if any of the structs isn't the right length, of ever becomes 
modified to not be the correct length.


Every way I've thought of looks like something that's just begging for 
errors to creep into the code, if not now then later...so *is* there a 
good way?