core14_loophole.hpp 7.6 KB

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