core_name20_static.hpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov.
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. // Initial implementation by Bela Schaum, https://github.com/schaumb
  6. // The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669
  7. //
  8. #ifndef BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP
  9. #define BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP
  10. #pragma once
  11. #include <boost/pfr/detail/config.hpp>
  12. #include <boost/pfr/detail/core.hpp>
  13. #include <boost/pfr/detail/sequence_tuple.hpp>
  14. #include <boost/pfr/detail/make_integer_sequence.hpp>
  15. #include <boost/pfr/detail/fields_count.hpp>
  16. #include <boost/pfr/detail/stdarray.hpp>
  17. #include <boost/pfr/detail/fake_object.hpp>
  18. #include <type_traits>
  19. #include <string_view>
  20. #include <array>
  21. #include <memory> // for std::addressof
  22. namespace boost { namespace pfr { namespace detail {
  23. struct core_name_skip {
  24. std::size_t size_at_begin;
  25. std::size_t size_at_end;
  26. bool is_backward;
  27. std::string_view until_runtime;
  28. consteval std::string_view apply(std::string_view sv) const noexcept {
  29. // We use std::min here to make the compiler diagnostic shorter and
  30. // cleaner in case of misconfigured BOOST_PFR_CORE_NAME_PARSING
  31. sv.remove_prefix((std::min)(size_at_begin, sv.size()));
  32. sv.remove_suffix((std::min)(size_at_end, sv.size()));
  33. if (until_runtime.empty()) {
  34. return sv;
  35. }
  36. const auto found = is_backward ? sv.rfind(until_runtime)
  37. : sv.find(until_runtime);
  38. const auto cut_until = found + until_runtime.size();
  39. const auto safe_cut_until = (std::min)(cut_until, sv.size());
  40. return sv.substr(safe_cut_until);
  41. }
  42. };
  43. struct backward {
  44. explicit consteval backward(std::string_view value) noexcept
  45. : value(value)
  46. {}
  47. std::string_view value;
  48. };
  49. consteval core_name_skip make_core_name_skip(std::size_t size_at_begin,
  50. std::size_t size_at_end,
  51. std::string_view until_runtime) noexcept
  52. {
  53. return core_name_skip{size_at_begin, size_at_end, false, until_runtime};
  54. }
  55. consteval core_name_skip make_core_name_skip(std::size_t size_at_begin,
  56. std::size_t size_at_end,
  57. backward until_runtime) noexcept
  58. {
  59. return core_name_skip{size_at_begin, size_at_end, true, until_runtime.value};
  60. }
  61. // it might be compilation failed without this workaround sometimes
  62. // See https://github.com/llvm/llvm-project/issues/41751 for details
  63. template <class>
  64. consteval std::string_view clang_workaround(std::string_view value) noexcept
  65. {
  66. return value;
  67. }
  68. template <class MsvcWorkaround, auto ptr>
  69. consteval auto name_of_field_impl() noexcept {
  70. // Some of the following compiler specific macro may be defined only
  71. // inside the function body:
  72. #ifndef BOOST_PFR_FUNCTION_SIGNATURE
  73. # if defined(__FUNCSIG__)
  74. # define BOOST_PFR_FUNCTION_SIGNATURE __FUNCSIG__
  75. # elif defined(__PRETTY_FUNCTION__) || defined(__GNUC__) || defined(__clang__)
  76. # define BOOST_PFR_FUNCTION_SIGNATURE __PRETTY_FUNCTION__
  77. # else
  78. # define BOOST_PFR_FUNCTION_SIGNATURE ""
  79. # endif
  80. #endif
  81. constexpr std::string_view sv = detail::clang_workaround<MsvcWorkaround>(BOOST_PFR_FUNCTION_SIGNATURE);
  82. static_assert(!sv.empty(),
  83. "====================> Boost.PFR: Field reflection parser configured in a wrong way. "
  84. "Please define the BOOST_PFR_FUNCTION_SIGNATURE to a compiler specific macro, "
  85. "that outputs the whole function signature including non-type template parameters."
  86. );
  87. constexpr auto skip = detail::make_core_name_skip BOOST_PFR_CORE_NAME_PARSING;
  88. static_assert(skip.size_at_begin + skip.size_at_end + skip.until_runtime.size() < sv.size(),
  89. "====================> Boost.PFR: Field reflection parser configured in a wrong way. "
  90. "It attempts to skip more chars than available. "
  91. "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section "
  92. "'Limitations and Configuration' for more information."
  93. );
  94. constexpr auto fn = skip.apply(sv);
  95. static_assert(
  96. !fn.empty(),
  97. "====================> Boost.PFR: Extraction of field name is misconfigured for your compiler. "
  98. "It skipped all the input, leaving the field name empty. "
  99. "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section "
  100. "'Limitations and Configuration' for more information."
  101. );
  102. auto res = std::array<char, fn.size()+1>{};
  103. auto* out = res.data();
  104. for (auto x: fn) {
  105. *out = x;
  106. ++out;
  107. }
  108. return res;
  109. }
  110. #ifdef __clang__
  111. #pragma clang diagnostic push
  112. #pragma clang diagnostic ignored "-Wundefined-var-template"
  113. // clang 16 and earlier don't support address of non-static member as template parameter
  114. // but fortunately it's possible to use C++20 non-type template parameters in another way
  115. // even in clang 16 and more older clangs
  116. // all we need is to wrap pointer into 'clang_wrapper_t' and then pass it into template
  117. template <class T>
  118. struct clang_wrapper_t {
  119. T v;
  120. };
  121. template <class T>
  122. clang_wrapper_t(T) -> clang_wrapper_t<T>;
  123. template <class T>
  124. constexpr auto make_clang_wrapper(const T& arg) noexcept {
  125. return clang_wrapper_t{arg};
  126. }
  127. #else
  128. template <class T>
  129. constexpr const T& make_clang_wrapper(const T& arg) noexcept {
  130. // It's everything OK with address of non-static member as template parameter support on this compiler
  131. // so we don't need a wrapper here, just pass the pointer into template
  132. return arg;
  133. }
  134. #endif
  135. template <class MsvcWorkaround, auto ptr>
  136. consteval auto name_of_field() noexcept {
  137. // Sanity check: known field name must match the deduced one
  138. static_assert(
  139. sizeof(MsvcWorkaround) // do not trigger if `name_of_field()` is not used
  140. && std::string_view{
  141. detail::name_of_field_impl<
  142. core_name_skip, detail::make_clang_wrapper(std::addressof(
  143. detail::fake_object<core_name_skip>().size_at_begin
  144. ))
  145. >().data()
  146. } == "size_at_begin",
  147. "====================> Boost.PFR: Extraction of field name is misconfigured for your compiler. "
  148. "It does not return the proper field name. "
  149. "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section "
  150. "'Limitations and Configuration' for more information."
  151. );
  152. return detail::name_of_field_impl<MsvcWorkaround, ptr>();
  153. }
  154. // Storing part of a string literal into an array minimizes the binary size.
  155. //
  156. // Without passing 'T' into 'name_of_field' different fields from different structures might have the same name!
  157. // See https://developercommunity.visualstudio.com/t/__FUNCSIG__-outputs-wrong-value-with-C/10458554 for details
  158. template <class T, std::size_t I>
  159. inline constexpr auto stored_name_of_field = detail::name_of_field<T,
  160. detail::make_clang_wrapper(std::addressof(detail::sequence_tuple::get<I>(
  161. detail::tie_as_tuple(detail::fake_object<T>())
  162. )))
  163. >();
  164. #ifdef __clang__
  165. #pragma clang diagnostic pop
  166. #endif
  167. template <class T, std::size_t... I>
  168. constexpr auto tie_as_names_tuple_impl(std::index_sequence<I...>) noexcept {
  169. return detail::sequence_tuple::make_sequence_tuple(std::string_view{stored_name_of_field<T, I>.data()}...);
  170. }
  171. template <class T, std::size_t I>
  172. constexpr std::string_view get_name() noexcept {
  173. static_assert(
  174. !std::is_union<T>::value,
  175. "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
  176. );
  177. static_assert(
  178. !std::is_array<T>::value,
  179. "====================> Boost.PFR: It is impossible to extract name from old C array since it doesn't have named members"
  180. );
  181. static_assert(
  182. sizeof(T) && BOOST_PFR_USE_CPP17,
  183. "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled."
  184. );
  185. return stored_name_of_field<T, I>.data();
  186. }
  187. template <class T>
  188. constexpr auto tie_as_names_tuple() noexcept {
  189. static_assert(
  190. !std::is_union<T>::value,
  191. "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
  192. );
  193. static_assert(
  194. !std::is_array<T>::value,
  195. "====================> Boost.PFR: It is impossible to extract name from old C array since it doesn't have named members"
  196. );
  197. static_assert(
  198. sizeof(T) && BOOST_PFR_USE_CPP17,
  199. "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled."
  200. );
  201. return detail::tie_as_names_tuple_impl<T>(detail::make_index_sequence<detail::fields_count<T>()>{});
  202. }
  203. }}} // namespace boost::pfr::detail
  204. #endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP