cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

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

Open pavelkryukov opened 2 years ago

pavelkryukov commented 2 years ago

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

Преобразование между тремя представлениями сделать сложно. Между первым и вторым конвертирует Type Loophole, но у этого трюка есть много ограничений, низкая скорость компиляции и т. д. Третье подключить можно, но с ещё большими ухищрениями (https://github.com/boostorg/pfr/issues/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 commented 2 years ago

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 commented 2 years ago

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

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

pavelkryukov commented 2 years ago

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

pavelkryukov commented 2 years ago

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

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

apolukhin commented 8 months ago

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

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

apolukhin commented 8 months ago

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