Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-11 08:18:45

0001 // Copyright (c) 2017-2018 Alexandr Poltavsky, Antony Polukhin.
0002 // Copyright (c) 2019-2024 Antony Polukhin.
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 
0008 // The Great Type Loophole (C++14)
0009 // Initial implementation by Alexandr Poltavsky, http://alexpolt.github.io
0010 //
0011 // Description:
0012 //  The Great Type Loophole is a technique that allows to exchange type information with template
0013 //  instantiations. Basically you can assign and read type information during compile time.
0014 //  Here it is used to detect data members of a data type. I described it for the first time in
0015 //  this blog post http://alexpolt.github.io/type-loophole.html .
0016 //
0017 // This technique exploits the http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118
0018 // CWG 2118. Stateful metaprogramming via friend injection
0019 // Note: CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.
0020 
0021 #ifndef BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
0022 #define BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
0023 #pragma once
0024 
0025 #include <boost/pfr/detail/config.hpp>
0026 
0027 #include <type_traits>
0028 #include <utility>
0029 
0030 #include <boost/pfr/detail/offset_based_getter.hpp>
0031 #include <boost/pfr/detail/fields_count.hpp>
0032 #include <boost/pfr/detail/make_flat_tuple_of_references.hpp>
0033 #include <boost/pfr/detail/make_integer_sequence.hpp>
0034 #include <boost/pfr/detail/sequence_tuple.hpp>
0035 #include <boost/pfr/detail/rvalue_t.hpp>
0036 #include <boost/pfr/detail/unsafe_declval.hpp>
0037 
0038 
0039 #ifdef __clang__
0040 #   pragma clang diagnostic push
0041 #   pragma clang diagnostic ignored "-Wmissing-braces"
0042 #   pragma clang diagnostic ignored "-Wundefined-inline"
0043 #   pragma clang diagnostic ignored "-Wundefined-internal"
0044 #   pragma clang diagnostic ignored "-Wmissing-field-initializers"
0045 #elif defined(__GNUC__)
0046 #   pragma GCC diagnostic push
0047 #   pragma GCC diagnostic ignored "-Wnon-template-friend"
0048 #endif
0049 
0050 
0051 namespace boost { namespace pfr { namespace detail {
0052 
0053 // tag<T,N> generates friend declarations and helps with overload resolution.
0054 // There are two types: one with the auto return type, which is the way we read types later.
0055 // The second one is used in the detection of instantiations without which we'd get multiple
0056 // definitions.
0057 
0058 template <class T, std::size_t N>
0059 struct tag {
0060     friend auto loophole(tag<T,N>);
0061 };
0062 
0063 // The definitions of friend functions.
0064 template <class T, class U, std::size_t N, bool B>
0065 struct fn_def_lref {
0066     friend auto loophole(tag<T,N>) {
0067         // Standard Library containers do not SFINAE on invalid copy constructor. Because of that std::vector<std::unique_ptr<int>> reports that it is copyable,
0068         // which leads to an instantiation error at this place.
0069         //
0070         // To workaround the issue, we check that the type U is movable, and move it in that case.
0071         using no_extents_t = std::remove_all_extents_t<U>;
0072         return static_cast< std::conditional_t<std::is_move_constructible<no_extents_t>::value, no_extents_t&&, no_extents_t&> >(
0073             boost::pfr::detail::unsafe_declval<no_extents_t&>()
0074         );
0075     }
0076 };
0077 template <class T, class U, std::size_t N, bool B>
0078 struct fn_def_rref {
0079     friend auto loophole(tag<T,N>) { return std::move(boost::pfr::detail::unsafe_declval< std::remove_all_extents_t<U>& >()); }
0080 };
0081 
0082 
0083 // Those specializations are to avoid multiple definition errors.
0084 template <class T, class U, std::size_t N>
0085 struct fn_def_lref<T, U, N, true> {};
0086 
0087 template <class T, class U, std::size_t N>
0088 struct fn_def_rref<T, U, N, true> {};
0089 
0090 
0091 // This has a templated conversion operator which in turn triggers instantiations.
0092 // Important point, using sizeof seems to be more reliable. Also default template
0093 // arguments are "cached" (I think). To fix that I provide a U template parameter to
0094 // the ins functions which do the detection using constexpr friend functions and SFINAE.
0095 template <class T, std::size_t N>
0096 struct loophole_ubiq_lref {
0097     template<class U, std::size_t M> static std::size_t ins(...);
0098     template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
0099 
0100     template<class U, std::size_t = sizeof(fn_def_lref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
0101     constexpr operator U&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
0102 };
0103 
0104 template <class T, std::size_t N>
0105 struct loophole_ubiq_rref {
0106     template<class U, std::size_t M> static std::size_t ins(...);
0107     template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
0108 
0109     template<class U, std::size_t = sizeof(fn_def_rref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
0110     constexpr operator U&&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
0111 };
0112 
0113 
0114 // This is a helper to turn a data structure into a tuple.
0115 template <class T, class U>
0116 struct loophole_type_list_lref;
0117 
0118 template <typename T, std::size_t... I>
0119 struct loophole_type_list_lref< T, std::index_sequence<I...> >
0120      // Instantiating loopholes:
0121     : sequence_tuple::tuple< decltype(T{ loophole_ubiq_lref<T, I>{}... }, 0) >
0122 {
0123     using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
0124 };
0125 
0126 
0127 template <class T, class U>
0128 struct loophole_type_list_rref;
0129 
0130 template <typename T, std::size_t... I>
0131 struct loophole_type_list_rref< T, std::index_sequence<I...> >
0132      // Instantiating loopholes:
0133     : sequence_tuple::tuple< decltype(T{ loophole_ubiq_rref<T, I>{}... }, 0) >
0134 {
0135     using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
0136 };
0137 
0138 
0139 // Lazily returns loophole_type_list_{lr}ref.
0140 template <bool IsCopyConstructible /*= true*/, class T, class U>
0141 struct loophole_type_list_selector {
0142     using type = loophole_type_list_lref<T, U>;
0143 };
0144 
0145 template <class T, class U>
0146 struct loophole_type_list_selector<false /*IsCopyConstructible*/, T, U> {
0147     using type = loophole_type_list_rref<T, U>;
0148 };
0149 
0150 template <class T>
0151 auto tie_as_tuple_loophole_impl(T& lvalue) noexcept {
0152     using type = std::remove_cv_t<std::remove_reference_t<T>>;
0153     using indexes = detail::make_index_sequence<fields_count<type>()>;
0154     using loophole_type_list = typename detail::loophole_type_list_selector<
0155         std::is_copy_constructible<std::remove_all_extents_t<type>>::value, type, indexes
0156     >::type;
0157     using tuple_type = typename loophole_type_list::type;
0158 
0159     return boost::pfr::detail::make_flat_tuple_of_references(
0160         lvalue,
0161         offset_based_getter<type, tuple_type>{},
0162         size_t_<0>{},
0163         size_t_<tuple_type::size_v>{}
0164     );
0165 }
0166 
0167 template <class T>
0168 auto tie_as_tuple(T& val) noexcept {
0169     static_assert(
0170         !std::is_union<T>::value,
0171         "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
0172     );
0173     return boost::pfr::detail::tie_as_tuple_loophole_impl(
0174         val
0175     );
0176 }
0177 
0178 template <class T, class F, std::size_t... I>
0179 void for_each_field_dispatcher(T& t, F&& f, std::index_sequence<I...>) {
0180     static_assert(
0181         !std::is_union<T>::value,
0182         "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
0183     );
0184     std::forward<F>(f)(
0185         boost::pfr::detail::tie_as_tuple_loophole_impl(t)
0186     );
0187 }
0188 
0189 }}} // namespace boost::pfr::detail
0190 
0191 
0192 #ifdef __clang__
0193 #   pragma clang diagnostic pop
0194 #elif defined(__GNUC__)
0195 #   pragma GCC diagnostic pop
0196 #endif
0197 
0198 
0199 #endif // BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
0200