Обнаружение первого члена типов POD для SFINAE - PullRequest
4 голосов
/ 24 мая 2019

Учитывая некоторые структуры POD, подобные этому:

struct StandardHeader
{
    uint32_t field1;
    uint32_t field2;
};

struct TypeA
{
    StandardHeader Header;
    uint8_t field3;
};

struct TypeB
{
    StandardHeader Header;
    uint16_t field4;
};

Я хотел бы написать черту типа (или что-то подобное, что может в конечном итоге использоваться в static_assert и std::enable_if или иным образом отключить шаблонный метод), которое может обнаружить существование поля StandardHeader в качестве первого член стандартного типа макета - т.е. такой, что reinterpret_cast<StandardHeader*>(&instance) безопасен.

(По сути, проверка базового типа is-a , за исключением того, что типы должны быть POD, что не будет истинным, если я использую фактическое наследование C ++.)

Мне удалось написать что-то, что использовало идиому обнаружения, чтобы убедиться, что тип является стандартным макетом и имеет Header член правильного типа:

template<typename, typename = std::void_t<>>
struct HasStandardHeader : std::false_type {};

template<typename T>
struct HasStandardHeader<T,
        std::void_t<decltype(std::declval<T>().Header)>>
    : std::conditional_t<
        std::is_standard_layout_v<T> &&
        std::is_same_v<decltype(std::declval<T>().Header), StandardHeader>
    , std::true_type, std::false_type> {};

Вышесказанное частично работает, но не подтверждает, что поле было первым.

Я попытался добавить что-то вроде этого выражения, чтобы обнаружить это, но это не работает:

static_cast<uint8_t*>(&static_cast<T*>(0)->Header) - static_cast<uint8_t*>(0)) == 0

(Другие неудачные попытки связаны с вызовом метода constexpr bool, но, к сожалению, они не действительны в conditional_t. Или, по крайней мере, при использовании & или чего-то еще.)

В идеале я бы предпочел что-то, что просто обнаруживает поле с правильным типом, там, даже не требуя, чтобы оно было названо Header. Это возможно? И есть ли лучший способ переписать вышесказанное?

Как упоминалось ранее, конечная цель состоит в том, чтобы сделать этот метод:

template<typename T>
bool Process(T& data, size_t len);

либо исчезнет, ​​либо static_assert, если T не относится к типу POD с правильным первым членом.


Редактировать : похоже, я немного усложнил это. Добавление этого выражения заставляет его работать как положено:

offsetof(T, Header) == 0

Но сейчас:

  • Есть ли лучший способ написать все это?
  • Можно ли написать это так, чтобы нам не требовалось, чтобы поле называлось Header?

1 Ответ

4 голосов
/ 24 мая 2019

Просто проверьте смещение члена.

template<typename, typename = std::void_t<>>
struct HasStandardHeader : std::false_type {};

template<typename T>
struct HasStandardHeader<T,
        std::void_t<decltype(std::declval<T>().Header)>>
    : std::conditional_t<
        std::is_standard_layout<T>::value &&
        std::is_same<decltype(std::declval<T>().Header), StandardHeader>::value &&
        offsetof(T, Header) == 0
    , std::true_type, std::false_type> {};

Протестировано немного на Годболт .

...