Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:40:38

0001 // Copyright (c) 2017-2018 Alexandr Poltavsky, Antony Polukhin.
0002 // Copyright (c) 2019-2025 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 #ifdef BOOST_PFR_HAS_STD_MODULE
0028 import std;
0029 #else
0030 #include <type_traits>
0031 #include <utility>
0032 #endif
0033 
0034 #include <boost/pfr/detail/offset_based_getter.hpp>
0035 #include <boost/pfr/detail/fields_count.hpp>
0036 #include <boost/pfr/detail/make_flat_tuple_of_references.hpp>
0037 #include <boost/pfr/detail/make_integer_sequence.hpp>
0038 #include <boost/pfr/detail/sequence_tuple.hpp>
0039 #include <boost/pfr/detail/rvalue_t.hpp>
0040 #include <boost/pfr/detail/unsafe_declval.hpp>
0041 
0042 
0043 #ifdef __clang__
0044 #   pragma clang diagnostic push
0045 #   pragma clang diagnostic ignored "-Wmissing-braces"
0046 #   pragma clang diagnostic ignored "-Wundefined-inline"
0047 #   pragma clang diagnostic ignored "-Wundefined-internal"
0048 #   pragma clang diagnostic ignored "-Wmissing-field-initializers"
0049 #elif defined(__GNUC__)
0050 #   pragma GCC diagnostic push
0051 #   pragma GCC diagnostic ignored "-Wnon-template-friend"
0052 #endif
0053 
0054 
0055 namespace boost { namespace pfr { namespace detail {
0056 
0057 // tag<T,N> generates friend declarations and helps with overload resolution.
0058 // There are two types: one with the auto return type, which is the way we read types later.
0059 // The second one is used in the detection of instantiations without which we'd get multiple
0060 // definitions.
0061 
0062 template <class T, std::size_t N>
0063 struct tag {
0064     friend auto loophole(tag<T,N>);
0065 };
0066 
0067 // The definitions of friend functions.
0068 template <class T, class U, std::size_t N, bool B>
0069 struct fn_def_lref {
0070     friend auto loophole(tag<T,N>) {
0071         // Standard Library containers do not SFINAE on invalid copy constructor. Because of that std::vector<std::unique_ptr<int>> reports that it is copyable,
0072         // which leads to an instantiation error at this place.
0073         //
0074         // To workaround the issue, we check that the type U is movable, and move it in that case.
0075         using no_extents_t = std::remove_all_extents_t<U>;
0076         return static_cast< std::conditional_t<std::is_move_constructible<no_extents_t>::value, no_extents_t&&, no_extents_t&> >(
0077             boost::pfr::detail::unsafe_declval<no_extents_t&>()
0078         );
0079     }
0080 };
0081 template <class T, class U, std::size_t N, bool B>
0082 struct fn_def_rref {
0083     friend auto loophole(tag<T,N>) { return std::move(boost::pfr::detail::unsafe_declval< std::remove_all_extents_t<U>& >()); }
0084 };
0085 
0086 
0087 // Those specializations are to avoid multiple definition errors.
0088 template <class T, class U, std::size_t N>
0089 struct fn_def_lref<T, U, N, true> {};
0090 
0091 template <class T, class U, std::size_t N>
0092 struct fn_def_rref<T, U, N, true> {};
0093 
0094 
0095 // This has a templated conversion operator which in turn triggers instantiations.
0096 // Important point, using sizeof seems to be more reliable. Also default template
0097 // arguments are "cached" (I think). To fix that I provide a U template parameter to
0098 // the ins functions which do the detection using constexpr friend functions and SFINAE.
0099 template <class T, std::size_t N>
0100 struct loophole_ubiq_lref {
0101     template<class U, std::size_t M> static std::size_t ins(...);
0102     template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
0103 
0104     template<class U, std::size_t = sizeof(fn_def_lref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
0105     constexpr operator U&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
0106 };
0107 
0108 template <class T, std::size_t N>
0109 struct loophole_ubiq_rref {
0110     template<class U, std::size_t M> static std::size_t ins(...);
0111     template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
0112 
0113     template<class U, std::size_t = sizeof(fn_def_rref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
0114     constexpr operator U&&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
0115 };
0116 
0117 
0118 // This is a helper to turn a data structure into a tuple.
0119 template <class T, class U>
0120 struct loophole_type_list_lref;
0121 
0122 template <typename T, std::size_t... I>
0123 struct loophole_type_list_lref< T, std::index_sequence<I...> >
0124      // Instantiating loopholes:
0125     : sequence_tuple::tuple< decltype(T{ loophole_ubiq_lref<T, I>{}... }, 0) >
0126 {
0127     using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
0128 };
0129 
0130 
0131 template <class T, class U>
0132 struct loophole_type_list_rref;
0133 
0134 template <typename T, std::size_t... I>
0135 struct loophole_type_list_rref< T, std::index_sequence<I...> >
0136      // Instantiating loopholes:
0137     : sequence_tuple::tuple< decltype(T{ loophole_ubiq_rref<T, I>{}... }, 0) >
0138 {
0139     using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
0140 };
0141 
0142 
0143 // Lazily returns loophole_type_list_{lr}ref.
0144 template <bool IsCopyConstructible /*= true*/, class T, class U>
0145 struct loophole_type_list_selector {
0146     using type = loophole_type_list_lref<T, U>;
0147 };
0148 
0149 template <class T, class U>
0150 struct loophole_type_list_selector<false /*IsCopyConstructible*/, T, U> {
0151     using type = loophole_type_list_rref<T, U>;
0152 };
0153 
0154 template <class T>
0155 auto tie_as_tuple_loophole_impl(T& lvalue) noexcept {
0156     using type = std::remove_cv_t<std::remove_reference_t<T>>;
0157     using indexes = detail::make_index_sequence<fields_count<type>()>;
0158     using loophole_type_list = typename detail::loophole_type_list_selector<
0159         std::is_copy_constructible<std::remove_all_extents_t<type>>::value, type, indexes
0160     >::type;
0161     using tuple_type = typename loophole_type_list::type;
0162 
0163     return boost::pfr::detail::make_flat_tuple_of_references(
0164         lvalue,
0165         offset_based_getter<type, tuple_type>{},
0166         size_t_<0>{},
0167         size_t_<tuple_type::size_v>{}
0168     );
0169 }
0170 
0171 template <class T>
0172 auto tie_as_tuple(T& val) noexcept {
0173     static_assert(
0174         !std::is_union<T>::value,
0175         "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
0176     );
0177     return boost::pfr::detail::tie_as_tuple_loophole_impl(
0178         val
0179     );
0180 }
0181 
0182 template <class T, class F, std::size_t... I>
0183 void for_each_field_dispatcher(T& t, F&& f, std::index_sequence<I...>) {
0184     static_assert(
0185         !std::is_union<T>::value,
0186         "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
0187     );
0188     std::forward<F>(f)(
0189         boost::pfr::detail::tie_as_tuple_loophole_impl(t)
0190     );
0191 }
0192 
0193 }}} // namespace boost::pfr::detail
0194 
0195 
0196 #ifdef __clang__
0197 #   pragma clang diagnostic pop
0198 #elif defined(__GNUC__)
0199 #   pragma GCC diagnostic pop
0200 #endif
0201 
0202 
0203 #endif // BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
0204