clang
выдает предупреждение, которое я не понимаю в данном контексте. Кажется, что указатель выровнен по 8 байтам, но clang
жалуются на то, что он выровнен по 4 байтам.
Чтобы это исправить, я могу привести его к тому же типу, что и указатель, что кажется излишним.
Что мне здесь не хватает? Что мне следует прочитать/понять и можно ли это исправить?
#include <memory>
typedef struct
{
int type;
} type_t __attribute__ ((aligned (8))); // 3rd party
void doNothing(type_t*) { }
int main() {
auto up = std::unique_ptr<type_t>(nullptr);
auto raw_ptr = up.get();
static_assert(alignof(type_t*) == 8);
static_assert(alignof(decltype(raw_ptr)) == 8);
doNothing(raw_ptr); // compile error: passing 4-byte aligned argument to 8-byte aligned parameter
doNothing(reinterpret_cast<type_t*>(raw_ptr)); // ok with cast
}
Я не уверен, что обмануло clang.
Но раз уж вы еще и спросили, как это исправить, то вот решение:
Вместо использования стиля C typedef ...
используйте стиль C++ без typedef
и определите struct
напрямую:
struct __attribute__ ((aligned (8))) type_t
{
int type;
};
Помимо решения проблемы предупреждения, этот стиль также считается лучшим стилем в C++.
alignof(type_t*) == 8
говорит вам, что выравнивание типа указателя выровнено по 8 байтам (что обычно справедливо в 64-битной системе), а не на то, что оно указывает на что-то, выровненное по 8 байтам.
С вашей программой и Clang, alignof(type_t) == 8
, но alignof(decltype(*raw_ptr)) == 4
.
Есть две формы __attribute__((aligned(N)))
. Один в структуре:
struct type_t __attribute__((aligned(8))) { int type; };
struct type_t { int type; } __attribute__((aligned(8)));
typedef struct { int type; } __attribute__((aligned(8))) type_t;
И один по имени типа:
typedef int more_aligned_int __attribute__ ((aligned (8)));
typedef struct type_t type_t __attribute__ ((aligned (8)));
typedef struct { int type; } type_t __attribute__((aligned(8)));
typedef struct type_t { int type; } type_t __attribute__((aligned(8)));
Тот, что в структуре, физически изменяет макет структуры, чтобы он был выровнен по 8 байтам:
typedef struct { int type; } __attribute__((aligned(8))) type_t;
static_assert(sizeof(type_t) == 8); // 4 padding bytes
static_assert(alignof(type_t) == 8);
Тот, что указан в typedef-name, меняет выравнивание только при доступе к нему через это имя:
typedef int more_aligned_int __attribute__ ((aligned (8)));
typedef struct type_t { int type; } type_t __attribute__((aligned(8)));
static_assert(sizeof(more_aligned_int) == 4);
static_assert(alignof(more_aligned_int) == 8);
static_assert(sizeof(type_t) == 4); // no padding bytes
static_assert(alignof(type_t) == 8);
static_assert(sizeof(struct type_t) == 4);
static_assert(alignof(struct type_t) == 4); // Not 8: The struct's alignment did not change
Когда вы передаете выровненную структуру в параметр шаблона, ничего не происходит, поскольку структура размещается по-другому, чтобы быть более выровненной.
Однако когда вы передаете в шаблоны имя typedef, выравнивание нарушается. std::vector<more_aligned_int>
имеет точно такой же тип, что и std::vector<int>
(атрибут aligned
ортогонален системе типов, это свойство typedef).
Clang удаляет выравнивание из имени typedef, что приводит к alignof(std::unique_ptr<type_t>::value_type) == 4
, поскольку это выравнивание структуры, а не имени typedef. Итак, .get()
возвращает невыровненный указатель, а raw_ptr
имеет тип ptr без выравнивания.
Если вы специально возвращаете его к имени typedef:
type_t* raw_ptr = up.get();
Оно будет снова выровнено.
Это может быть ошибка clang, поскольку она отличается от того, что делает GCC (который сохраняет выравнивание даже при передаче в качестве аргумента шаблона). Однако поведение Клана более последовательное.
Атрибут находится в неправильном месте для применения к структуре:
typedef struct
{
int type;
} __attribute__ ((aligned (8))) type_t;
// Now: sizeof(type_t) == 8, so you can have arrays of type_t.
Сообщите об этом своему стороннему поставщику библиотеки.
к сожалению, это сторонняя библиотека, поэтому я не могу это изменить.