Смещение указателя на элемент

#c #templates #member-pointers

#c #шаблоны #указатели на элементы

Вопрос:

 template<class T, typename U> ptrdiff_t foo(T U::* m)
{
    // return offset
}
  

Как я могу получить смещение поля ‘m’ в этом контексте? Я бы предпочел использовать выражение во время компиляции.

Заранее спасибо за любую помощь. С наилучшими пожеланиями

Комментарии:

1. Вероятно, это плохая идея. Почему бы вместо этого не использовать тип указателя на элемент?

2. Хм, что именно ты имеешь в виду? Приведенный выше параметр ЯВЛЯЕТСЯ указателем на элемент …

3. Правильно, и оставьте все как есть. Смещения просто слишком примитивны, чтобы использоваться для большинства классов C (не POD).

4. Хорошо, но вы написали ‘почему бы вместо этого не использовать тип указателя на элемент? Что вы это имеете в виду? Кстати.: Я мог бы написать специфичную для компилятора версию моей функции member_offset.

5. @GManNickG Vulkan, OpenGL и любые другие графические библиотеки, использующие структурированные буферы, запрашивают смещения вместо указателей на элемент. В этом случае они не понимают указатели на элемент, и вы должны перевести это в смещение.

Ответ №1:

@Michael J

Спасибо за ваш ответ. Это было не совсем то, что я искал, но это вдохновило меня на это:

 template<class T, typename U>
std::ptrdiff_t member_offset(U T::* member)
{
    return reinterpret_cast<std::ptrdiff_t>(
        amp;(reinterpret_cast<T const volatile*>(NULL)->*member)
    );
}
  

Комментарии:

1. Здесь вы разыменовываете нулевой указатель. Это недопустимо, если только вы не внедряете библиотеку и не получаете специальное разрешение на использование в макросе offsetof. Кто-то опередил меня с -1.

2. Вот что делает макрос offsetof: (size_t)amp;reinterpret_cast<постоянный изменяемый символamp;>((((s *)0)->m)). В чем разница?

3. Разница в том, что offsetof делает это для отдельного компилятора , и только потому, что разработчик библиотеки заключил специальную сделку с автором компилятора. Тогда это могло бы сработать. В других компиляторах вместо offsetof используется специальная __встроенная_функция offsetof. Или какой-нибудь другой трюк. Основная причина наличия offsetof в библиотеке заключается в том, что вы не можете записать ее переносимо, вам нужна специальная поддержка со стороны компилятора.

4. Как я уже говорил в комментарии выше, offsetof и kin — плохие идеи в C . Используйте указатель на элементы.

Ответ №2:

Звучит так, как будто вы ищете макрос offsetof().

Комментарии:

1. Недопустимо вызывать offsetof с указателем на элемент, только с именем элемента. (во многих реализациях это может сработать, но, учитывая, насколько неясны такого рода вещи, это может привести к сбою при любых обновлениях компилятора)

2. Привет @Yakk. Я не совсем уверен, что вы имеете в виду. Если вы нажмете на слово «offsetof» (выше), это приведет вас к документам для макроса. Мне это кажется кошерным. Под «указателем на элемент» вы подразумеваете элемент структуры, который является указателем? например, struct s { int i; char *p; }; size_t n = offsetof(struct s, p);

3. Вы не можете использовать offsetof() , если указатель на элемент является аргументом шаблона.

Ответ №3:

Вы можете получить смещение, но это не бесплатно:

 template <typename T, class C>
size_t memberOffset(T C::*member)
{
    C c {};
    return size_t(amp;(c.*member)) - size_t(amp;c);
}

// usage
struct Vector {
    int x;
    int y;
};

size_t off = memberOffset(amp;Vector::y);
  

К сожалению, как вы можете видеть, это не constexpr , и поэтому его нельзя использовать во всех сценариях, которые вы можете захотеть. Он также имеет (очень маленькие) накладные расходы, но, похоже, компилятор просто полностью оптимизирует его: https://godbolt.org/z/jGeox9 .

Если вам интересно, можете ли вы просто разбрызгать constexpr везде самостоятельно и заставить это работать, вы можете, и ваш компилятор мог бы даже скомпилировать его и запустить, но использование приведения к size_t недопустимо в constexpr несмотря на известный дефект, который допускают многие компиляторы.

Заслуги за этот метод принадлежат не мне, а Дэниелу Вейлеру и его превосходной сути: https://gist.github.com/graphitemaster/494f21190bb2c63c5516

Ответ №4:

Простой ответ заключается в том, что вы не можете. Если тип U является POD , вы можете использовать макрос offsetof , но формально, это неопределенное поведение, если тип не является POD: в зависимости от компилятора, вы получите ошибку во время компиляции или просто неправильные результаты иногда. И вы не можете использовать это для указателя на элемент. Вы должны вызвать его с именем класса и именем элемента.