Search code examples
structd

Writing a raw binary structure to file in D?


I'm trying to have a binary file which contains several binary records defined in some struct. However, I do cannot seem to find how to do it. Looking at other examples, I've managed to write strings without problems, but not struct. I just want to write it like I would in C with fwrite(3), but in D version 2.

Here is what I've tried so far:

  • using stream.write(tr) - writes human readable/debug representation
  • using stream.rawWrite(tr) - this sounded like what I need, but fails to compile with:

Error: template std.stdio.File.rawWrite cannot deduce function from argument types !()(TitleRecord), candidates are:

/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(1132): std.stdio.File.rawWrite(T)(in T[] buffer)

  • trying rawWrite as above, but casting data to various things, also never compiles.
  • even trying to get back to C with fwrite, but can't get deep enough to get file descriptor I need.

Reading the docs has not been very helpful (writing strings works for me too, but not writing struct). I'm sure there must be simple way to do it, but I'm not able to find it.... Other SO questions did not help me. I D 1.0, it might have been accomplished with stream.writeExact(&tr, tr.sizeof) but that is no longer an option.

import std.stdio;

struct TitleRecord {
        short id;
        char[49] text;
};

TitleRecord tr;

void main()
{
 auto stream = File("filename.dat","wb+");
 tr.id = 1234;
 tr.text = "hello world";
 writeln(tr);
 //stream.write(tr);
 //stream.rawWrite(tr);
 //stream.rawWrite(cast(ubyte[52]) tr);
 //stream.rawWrite(cast(ubyte[]) tr);
 //fwrite(&tr, 4, 1, stream);
}

Solution

  • For this that error is saying it expects an array not a struct. So one easy way to do it is to simply slice a pointer and give that to rawWrite:

    stream.rawWrite((&tr)[0 .. 1]);
    

    The (&tr) gets the address, thus converting your struct to a pointer. Then the [0 .. 1] means get a slice of it from the beginning, grabbing just one element.

    Thus you now have a T[] that rawWrite can handle containing your one element.

    Be warned if you use the @safe annotation this will not pass, you'd have to mark it @trusted. Also of course any references inside your struct (including string) will be written as binary pointers instead of data as you surely know from C experience. But in the case you showed there you're fine.

    edit: BTW you could also just use fwrite if you like, copy/pasting the same code over from C (except it is foo.sizeof instead of sizeof foo). The D File thing is just a small wrapper around C's FILE* and you can get the original FILE* back out to pass to the other functions with stream.getFP() http://dpldocs.info/experimental-docs/std.stdio.File.getFP.html )