Определение структур переменной длины и приведение к ним - PullRequest
2 голосов
/ 10 июня 2011

В C вы иногда видите что-то вроде:

struct foobar
{
    int size;
    int data[1];
};

, где data член на самом деле не имеет только одного элемента; скорее это должно быть переменной длины. Если вы сделаете что-то подобное в D, это позволит вам, например, прочитать myfoobar.data[4]? Я знаю, что D имеет массивы переменной длины, например, int[] myvarlenintarray;, но что, если вы пытаетесь взаимодействовать с каким-то кодом, который уже выдает в память структуру данных, подобную приведенной выше, и, возможно, гораздо более сложную, чем эта? Допустим, это в первой части int[3000] buffer;. Есть ли простой способ привести его к пригодной для использования структуре, не перемещая ее в память? Если нет, то есть ли простой способ получить данные в аналогичную структуру, не анализируя каждый член структуры вручную?

редактирование:

Я думаю, мне нужно привести практический пример, чтобы вы видели, откуда я.

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.

}

Во-первых, я не понимаю, почему это отличается для случая, когда я не даю *ReparseDataPtr имя.

Во-вторых, нет ли способа получить символ, тип которого REPARSE_DATA_BUFFER, а данные которого расположены в reparsebuffer.ptr?

Ответы [ 2 ]

3 голосов
/ 10 июня 2011

Вы пытались сделать то же самое в D, что и в C?

struct foobar { int size; int data[1]; };

Это работает ... просто используйте data.ptr вместо data для доступа к элементам, потому что в противном случае это будетвыполнить проверку границ длиной 1.

1 голос
/ 10 июня 2011

Вы можете получить к нему доступ с помощью вспомогательного метода:

struct foobar
{
 public:
  int[] Data() { return data.ptr[0..size]; }

 private:
  int size;
  int data[1];
}

Возможно, вы также захотите поместить int в foreach над элементами foobar, использующими static assert, чтобы убедиться, что смещение каждого меньше смещения data.

...