Defining variable length structs and casting to them
In C, you sometimes see something like:
struct foobar
{
int size;
int data[1];
};
where the data
member doesn't really have just one element; rather it's meant to be variable length.
If you do something like that in D, is it going to let you, for example, read myfoobar.data[4]
?
I know D has variable length arrays, e.g. int[] myvarlenintarray;
, but what if you're trying to interface with some code that already puts out a data structure in memory like the one above, and possibly much more complex than that? Let's say it's in the first portion of int[3000] buffer;
. Is there an easy way to cast it to a usable struct without moving it in memory? If not, is there an easy way to get the data into a similar struct without having to manually parse out each member of the struct?
edit:
I think I need to give a practical example so you see where I'm coming from.
import std.c.windows.windows;
import std.utf;
import std.stdio;
public struct REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct SymbolicLinkReparseBuffer
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR[1] PathBuffer;
}
SymbolicLinkReparseBuffer mySymbolicLinkReparseBuffer;
struct MountPointReparseBuffer
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR[1] PathBuffer;
}
MountPointReparseBuffer myMountPointReparseBuffer;
struct GenericReparseBuffer
{
UCHAR[1] DataBuffer;
}
GenericReparseBuffer myGenericReparseBuffer;
}
}
alias REPARSE_DATA_BUFFER* PREPARSE_DATA_BUFFER;
enum MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16*1024;
// Values for 'ReparseTag' member of REPARSE_DATA_BUFFER:
enum : DWORD {
IO_REPARSE_TAG_SYMLINK = 0xA000000C,
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 // which also defines a Junction Point
}
enum DWORD FSCTL_GET_REPARSE_POINT = 0x000900a8;
enum FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
public extern(Windows) BOOL function(HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*) DeviceIoControl;
void main()
{
DeviceIoControl = cast(BOOL function(HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*))GetProcAddress(LoadLibraryA("kernel32.dll"), "DeviceIoControl");
auto RPHandle = CreateFileW((r"J:\Documents and Settings").toUTF16z(), 0, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT + FILE_FLAG_BACKUP_SEMANTICS, null);
if (RPHandle == INVALID_HANDLE_VALUE)
{
printf("CreateFileW failed with error code %d.", GetLastError());
return;
}
BYTE[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] reparsebuffer;
uint reparsedatasize;
auto getreparsepointresult = DeviceIoControl(RPHandle, FSCTL_GET_REPARSE_POINT, null, 0, cast(void*) reparsebuffer.ptr, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &reparsedatasize, null);
if (getreparsepointresult == 0)
{
printf("DeviceIoControl with FSCTL_GET_REPARSE_POINT failed with error code %d.", GetLastError());
return;
}
// Now what?
// If I do this:
auto ReparseDataPtr = cast(REPARSE_DATA_BUFFER*) reparsebuffer.ptr;
printf("%d == %d\n", reparsebuffer.ptr, ReparseDataPtr); // Alright, data hasn't been copied.
// But what good is a pointer? Can I use a pointer to a struct to access one of its members apart from dereferencing?
printf("%d == %d\n", &reparsebuffer[0], &(*ReparseDataPtr)); // Here, I dereference ReparseDataPtr, but nothing moves.
printf("%d == %d\n", &reparsebuffer[0], &((*ReparseDataPtr).ReparseTag)); // Same here, so I can access members in a roundabout way.
printf("%d == %d\n", &reparsebuffer[0], &(ReparseDataPtr.ReparseTag)); // And thanks to Jim's comment, here's a less roundabout way.
auto ReparseData = *ReparseDataPtr; // But if I assign a name to the dereferenced ReparseDataPtr,
printf("%d != %d\n", &reparsebuffer[0], &(ReparseData.ReparseTag)); // the data is copied to a new location, leaving most of PathBuffer behind.
REPARSE_DATA_BUFFER ReparseDataFn() {return *ReparseDataPtr;} // Similarly, this way
printf("%d != %d\n", &reparsebuffer[0], &(ReparseDataFn().ReparseTag)); // copies stuff to a new location.
}
Firstly, I don't understand why it's diffe开发者_开发百科rent for the case in which I don't give *ReparseDataPtr
a name.
Secondly, is there no way to have a symbol whose type is REPARSE_DATA_BUFFER and whose data is located at reparsebuffer.ptr?
Have you tried doing the exact same thing in D as in C?
struct foobar { int size; int data[1]; };
It works... just use data.ptr
instead of data
to access the elements, because otherwise it will perform bounds checking with a length of 1.
You could access it via a helper method:
struct foobar
{
public:
int[] Data() { return data.ptr[0..size]; }
private:
int size;
int data[1];
}
You might also want to put int a static foreach
over the members of foobar
that uses static assert
to make sure that the offset of each is less than the offset of data
.
精彩评论