]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/pfr/detail/core14_loophole.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / pfr / detail / core14_loophole.hpp
1 // Copyright (c) 2017-2018 Alexandr Poltavsky, Antony Polukhin.
2 // Copyright (c) 2019-2020 Antony Polukhin.
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7
8 // The Great Type Loophole (C++14)
9 // Initial implementation by Alexandr Poltavsky, http://alexpolt.github.io
10 //
11 // Description:
12 // The Great Type Loophole is a technique that allows to exchange type information with template
13 // instantiations. Basically you can assign and read type information during compile time.
14 // Here it is used to detect data members of a data type. I described it for the first time in
15 // this blog post http://alexpolt.github.io/type-loophole.html .
16 //
17 // This technique exploits the http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118
18 // CWG 2118. Stateful metaprogramming via friend injection
19 // Note: CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.
20
21 #ifndef BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
22 #define BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
23
24 #include <boost/pfr/detail/config.hpp>
25
26 #include <type_traits>
27 #include <utility>
28
29 #include <boost/pfr/detail/cast_to_layout_compatible.hpp> // still needed for enums
30 #include <boost/pfr/detail/offset_based_getter.hpp>
31 #include <boost/pfr/detail/fields_count.hpp>
32 #include <boost/pfr/detail/make_flat_tuple_of_references.hpp>
33 #include <boost/pfr/detail/make_integer_sequence.hpp>
34 #include <boost/pfr/detail/sequence_tuple.hpp>
35 #include <boost/pfr/detail/rvalue_t.hpp>
36 #include <boost/pfr/detail/unsafe_declval.hpp>
37
38
39 #ifdef __clang__
40 # pragma clang diagnostic push
41 # pragma clang diagnostic ignored "-Wmissing-braces"
42 # pragma clang diagnostic ignored "-Wundefined-inline"
43 # pragma clang diagnostic ignored "-Wundefined-internal"
44 # pragma clang diagnostic ignored "-Wmissing-field-initializers"
45 #elif defined(__GNUC__)
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wnon-template-friend"
48 #endif
49
50
51 namespace boost { namespace pfr { namespace detail {
52
53 // tag<T,N> generates friend declarations and helps with overload resolution.
54 // There are two types: one with the auto return type, which is the way we read types later.
55 // The second one is used in the detection of instantiations without which we'd get multiple
56 // definitions.
57
58 template <class T, std::size_t N>
59 struct tag {
60 friend auto loophole(tag<T,N>);
61 };
62
63 // The definitions of friend functions.
64 template <class T, class U, std::size_t N, bool B>
65 struct fn_def_lref {
66 friend auto loophole(tag<T,N>) {
67 // Standard Library containers do not SFINAE on invalid copy constructor. Because of that std::vector<std::unique_ptr<int>> reports that it is copyable,
68 // which leads to an instantiation error at this place.
69 //
70 // To workaround the issue, we check that the type U is movable, and move it in that case.
71 using no_extents_t = std::remove_all_extents_t<U>;
72 return static_cast< std::conditional_t<std::is_move_constructible<no_extents_t>::value, no_extents_t&&, no_extents_t&> >(
73 boost::pfr::detail::unsafe_declval<no_extents_t&>()
74 );
75 }
76 };
77 template <class T, class U, std::size_t N, bool B>
78 struct fn_def_rref {
79 friend auto loophole(tag<T,N>) { return std::move(boost::pfr::detail::unsafe_declval< std::remove_all_extents_t<U>& >()); }
80 };
81
82
83 // Those specializations are to avoid multiple definition errors.
84 template <class T, class U, std::size_t N>
85 struct fn_def_lref<T, U, N, true> {};
86
87 template <class T, class U, std::size_t N>
88 struct fn_def_rref<T, U, N, true> {};
89
90
91 // This has a templated conversion operator which in turn triggers instantiations.
92 // Important point, using sizeof seems to be more reliable. Also default template
93 // arguments are "cached" (I think). To fix that I provide a U template parameter to
94 // the ins functions which do the detection using constexpr friend functions and SFINAE.
95 template <class T, std::size_t N>
96 struct loophole_ubiq_lref {
97 template<class U, std::size_t M> static std::size_t ins(...);
98 template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
99
100 template<class U, std::size_t = sizeof(fn_def_lref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
101 constexpr operator U&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
102 };
103
104 template <class T, std::size_t N>
105 struct loophole_ubiq_rref {
106 template<class U, std::size_t M> static std::size_t ins(...);
107 template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) > static char ins(int);
108
109 template<class U, std::size_t = sizeof(fn_def_rref<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
110 constexpr operator U&&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior.
111 };
112
113
114 // This is a helper to turn a data structure into a tuple.
115 template <class T, class U>
116 struct loophole_type_list_lref;
117
118 template <typename T, std::size_t... I>
119 struct loophole_type_list_lref< T, std::index_sequence<I...> >
120 // Instantiating loopholes:
121 : sequence_tuple::tuple< decltype(T{ loophole_ubiq_lref<T, I>{}... }, 0) >
122 {
123 using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
124 };
125
126
127 template <class T, class U>
128 struct loophole_type_list_rref;
129
130 template <typename T, std::size_t... I>
131 struct loophole_type_list_rref< T, std::index_sequence<I...> >
132 // Instantiating loopholes:
133 : sequence_tuple::tuple< decltype(T{ loophole_ubiq_rref<T, I>{}... }, 0) >
134 {
135 using type = sequence_tuple::tuple< decltype(loophole(tag<T, I>{}))... >;
136 };
137
138
139 // Lazily returns loophole_type_list_{lr}ref.
140 template <bool IsCopyConstructible /*= true*/, class T, class U>
141 struct loophole_type_list_selector {
142 using type = loophole_type_list_lref<T, U>;
143 };
144
145 template <class T, class U>
146 struct loophole_type_list_selector<false /*IsCopyConstructible*/, T, U> {
147 using type = loophole_type_list_rref<T, U>;
148 };
149
150 template <class T>
151 auto tie_as_tuple_loophole_impl(T& lvalue) noexcept {
152 using type = std::remove_cv_t<std::remove_reference_t<T>>;
153 using indexes = detail::make_index_sequence<fields_count<type>()>;
154 using loophole_type_list = typename detail::loophole_type_list_selector<
155 std::is_copy_constructible<std::remove_all_extents_t<type>>::value, type, indexes
156 >::type;
157 using tuple_type = typename loophole_type_list::type;
158
159 return boost::pfr::detail::make_flat_tuple_of_references(
160 lvalue,
161 offset_based_getter<type, tuple_type>{},
162 size_t_<0>{},
163 size_t_<tuple_type::size_v>{}
164 );
165 }
166
167 template <class T>
168 auto tie_as_tuple(T& val) noexcept {
169 static_assert(
170 !std::is_union<T>::value,
171 "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
172 );
173 return boost::pfr::detail::tie_as_tuple_loophole_impl(
174 val
175 );
176 }
177
178 template <class T, class F, std::size_t... I>
179 void for_each_field_dispatcher(T& t, F&& f, std::index_sequence<I...>) {
180 static_assert(
181 !std::is_union<T>::value,
182 "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
183 );
184 std::forward<F>(f)(
185 boost::pfr::detail::tie_as_tuple_loophole_impl(t)
186 );
187 }
188
189 }}} // namespace boost::pfr::detail
190
191
192 #ifdef __clang__
193 # pragma clang diagnostic pop
194 #elif defined(__GNUC__)
195 # pragma GCC diagnostic pop
196 #endif
197
198
199 #endif // BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP
200