Требует ли стандарт, чтобы объекты в автоматическом хранилище имели правильное выравнивание для любого типа (например, как это делает malloc)? - PullRequest
7 голосов
/ 23 марта 2012

Мне любопытно, требуется ли для правильного выравнивания для любого типа выделение буфера в стеке, аналогично тому, как работает malloc, или если мне придется использовать что-то вроде std::aligned_storage.

Рассмотрим следующий блок кода:

typedef enum _KEY_VALUE_INFORMATION_CLASS {
    KeyValueBasicInformation            = 0,
    // Others
} KEY_VALUE_INFORMATION_CLASS;

typedef struct _KEY_VALUE_BASIC_INFORMATION {
    ULONG TitleIndex;
    ULONG Type;
    ULONG NameLength;
    WCHAR Name[1];
} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;

std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
    std::vector<std::wstring> result;
    ULONG index = 0;
    const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
        sizeof(KEY_VALUE_BASIC_INFORMATION);

    // Stack buffer here
    unsigned char buff[valueNameStructSize];
    // Casted here
    KEY_VALUE_BASIC_INFORMATION const* basicValueInformation =
        reinterpret_cast<KEY_VALUE_BASIC_INFORMATION const*>(buff);
    for(;;)
    {
        ULONG resultLength;
        NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
            hKey_,
            index++,
            KeyValueBasicInformation,
            buff,
            valueNameStructSize,
            &resultLength);
        if (NT_SUCCESS(errorCheck))
        {
            result.emplace_back(std::wstring(basicValueInformation->Name,
                basicValueInformation->NameLength / sizeof(wchar_t)));
        }
        else if (errorCheck == STATUS_NO_MORE_ENTRIES)
        {
            break;
        }
        else
        {
            Win32Exception::ThrowFromNtError(errorCheck);
        }
    }
    return result;
}

Обратите внимание, как значение buff представляет собой буфер символов, помещенный в стек, размер которого соответствует данному максимальному объему данных.Однако я обеспокоен тем, что приведение, требуемое для интерпретации буфера как строки, может вызвать ошибку выравнивания, если этот код будет перенесен на другую (скажем, ARM или IA64) платформу.

РЕДАКТИРОВАТЬ: Если кому-то интересноЯ переделал это в терминах std::aligned_storage и std::alignment_of:

std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
    std::vector<std::wstring> result;
    ULONG index = 0;
    const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
        sizeof(KEY_VALUE_BASIC_INFORMATION);
    std::aligned_storage<valueNameStructSize,
        std::alignment_of<KEY_VALUE_BASIC_INFORMATION>::value>::type buff;
    auto basicValueInformation =
        reinterpret_cast<KEY_VALUE_BASIC_INFORMATION*>(&buff);
    for(;;)
    {
        ULONG resultLength;
        NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
            hKey_,
            index++,
            KeyValueBasicInformation,
            basicValueInformation,
            valueNameStructSize,
            &resultLength);
        if (NT_SUCCESS(errorCheck))
        {
            result.emplace_back(std::wstring(basicValueInformation->Name,
                basicValueInformation->NameLength / sizeof(wchar_t)));
        }
        else if (errorCheck == STATUS_NO_MORE_ENTRIES)
        {
            break;
        }
        else
        {
            Win32Exception::ThrowFromNtError(errorCheck);
        }
    }
    return std::move(result);
}

1 Ответ

5 голосов
/ 23 марта 2012

Стандарт не предъявляет требований к выравниванию автоматических переменных (или переменных со статическим хранилищем в этом отношении), кроме того, что компилятор должен убедиться, что доступ к ним работает.

  • C ++ 03 3.9 / 5 типов

Типы объектов имеют требования к выравниванию (3.9.1, 3.9.2). выравнивание полного типа объекта определяется реализацией целочисленное значение, представляющее количество байтов; объект выделен по адресу, который соответствует требованиям выравнивания его типа объекта

Примечание: «тип объекта» здесь означает тип, который не является функцией, ссылочным или пустым типом (то есть он применяется к unsigned char).

Одним из способов получения выровненного буфера может быть объявление buff примерно так:

KEY_VALUE_BASIC_INFORMATION buff[valueNameStructSize/sizeof(KEY_VALUE_BASIC_INFORMATION) + 1];

И вы сможете избавиться от reinterpret_cast<> для загрузки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...