Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:43:32

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