Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Рефлексия над pointer-to-member на этапе компиляции #482

Open
pavelkryukov opened this issue Oct 24, 2021 · 6 comments
Labels
Помощь советом Идея прорабатывается другими участниками международного коммитета

Comments

@pavelkryukov
Copy link

pavelkryukov commented Oct 24, 2021

У поля структуры есть три базовых представления:

  • порядковый номер в структуре (индекс)
  • смещение относительно начала структуры (offsetof)
  • pointer-to-member

Преобразование между тремя представлениями сделать сложно. Между первым и вторым конвертирует Type Loophole, но у этого трюка есть много ограничений, низкая скорость компиляции и т. д. Третье подключить можно, но с ещё большими ухищрениями (boostorg/pfr#60).

При этом последнее представление и наиболее полно, и наиболее надёжно реализовано в C++ (и С).
Предлагается считать его основным, и попросить компилятор объявить переменные, через которые определяются все преобразования:

namespace ptm { // pointer-to-member

template<typename T>
constexpr size_t number_of_members = $$$;

template<typename T, typename R, R T::* member>
constexpr size_t index_of_member = $$$;

template<typename T, size_t N>
struct type_of_member { using type = $$$; };

template<typename T, size_t N, typename R = type_of_member<T, N>::type>
struct type_of_pointer_to_member { using type = R T::*; }

template<typename T, size_t N>
constexpr type_of_pointer_to_member<T, N> pointer_to_member = $$$;

} // namespace ptm

В результате имплементация Boost.PFR сильно сократится:

namespace details {

template<typename T, int ... N>
auto structure_tie(T& object, std::index_sequence<N...>)
{
    return std::tie(*(object.*ptm::pointer_to_member<T,N>)...);
}

} // namespace details

template<typename T>
auto structure_tie(T& object)
{
    return details::structure_tie(object, std::make_index_sequence<ptm::number_of_members<T>>{});
}

Отсюда же порождается имплементация #468:

namespace details {

template<typename T, int ... N>
constexpr auto tuple_of_memptrs(std::index_sequence<N...>)
{
    return std::tuple(ptm::pointer_to_member<T,N>)...);
}

} // namespace details

template<typename T>
constexpr auto tuple_of_memptrs()
{
    return details::tuple_of_memptrs(std::make_index_sequence<ptm::number_of_members<T>>());
}

Не самым оптимальным образом, но порождается PR0908

namespace details {

template<size_t I, typename T, typename R>
constexpr auto offsetof_impl(R T::* member)
{
    static_assert(I < ptm::number_of_members<T>);
    if constexpr (!std::is_same_v<R, ptm::type_of_member<T, I>>)
        return offsetof_impl<I + 1>(member);
    else if (ptm::pointer_to_member<T, I> != member)
        return offsetof_impl<I + 1>(member);
    else
        return offsetof(T, ptm::pointer_to_member<T, I>);
}

} // namespace details

template<typename T, typename R>
auto offsetof_dynamic(R T::* member)
{
    return details::offsetof_impl<0>(member);
}

index_of_member может быть использован для метапрограммирования нестандартного расположения полей (SoA, упаковка и т. д.)

@pavelkryukov
Copy link
Author

pavelkryukov commented Nov 5, 2021

Reflection TS, если я его верно понял, имеет numer_of_members:

template <ObjectSequence T> struct get_size;
All specializations of get_size<T> shall meet the UnaryTypeTrait requirements (20.10.1)
with a base characteristic of integral_constant<size_t, N>, where N is the number of elements in the object sequence.

и type_of_member:

template <size_t I, ObjectSequence T> struct get_element;
All specializations of get_element<I, T> shall meet the TransformationTrait requirements
(20.10.1). The nested type named type corresponds to the Ith element Object in T, where
the indexing is zero-based.

Однако, вещей, похожих на pointer_to_member и index_of_member я не нашёл.

@Izaron
Copy link

Izaron commented Jan 5, 2022

На самом деле эту тему переиграли и вроде как рефлексию собираются делать через constexpr-объекты, а не шаблоны. Я написал что и где, возможно надо будет пересмотреть идею (?)
https://habr.com/ru/post/598981/

А так - можно сделать вагон крутых идей, но неясно, дойдёт ли это до SG7. У меня такое ощущение, что эту лямку Andrew Sutton тянет чуть ли не в соло.

@pavelkryukov
Copy link
Author

За годы немало предложений свёрнуто с формулировкой «дождитесь интроспекции/рефлексии». Нужна какая-то проактивная позиция на этот счёт; я ничего не могу предложить лучше чем принести на стол больше мотивационных примеров.

@pavelkryukov
Copy link
Author

вроде как рефлексию собираются делать через constexpr-объекты, а не шаблоны

Всё же издам глас вопиющего в пустыне: зачем сходить с накатанной лыжни?
В языке уже есть шаблоно-подобные std::is_enum, std::is_destructible, std::is_same...

@apolukhin
Copy link
Member

В языке уже есть шаблоно-подобные std::is_enum, std::is_destructible, std::is_same...

Они очень плохо подходят для рефлексии. Использование шаблона его инстанцирует, инстанс при компиляции потребляет оперативную память компилятора за счёт увеличения количества элементов во внутренних структур компилятора. Структуры с большим количеством элементов тормозят т.к. замедляют поиски и ухудшают поподания в кеш. Если рефлексия инстанцирует шаблоны и одновременно работает с контейнерами хранящими инстансы - возникают сложности с инвалидацией указателей внутри компилятора

@apolukhin
Copy link
Member

Предложение по рефлексии https://wg21.link/P2996

@apolukhin apolukhin added the Помощь советом Идея прорабатывается другими участниками международного коммитета label Oct 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Помощь советом Идея прорабатывается другими участниками международного коммитета
Projects
None yet
Development

No branches or pull requests

3 participants