开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜