offset_based_getter.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright (c) 2017-2018 Chris Beck
  2. // Copyright (c) 2019-2025 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. #ifndef BOOST_PFR_DETAIL_OFFSET_BASED_GETTER_HPP
  7. #define BOOST_PFR_DETAIL_OFFSET_BASED_GETTER_HPP
  8. #pragma once
  9. #include <boost/pfr/detail/config.hpp>
  10. #include <boost/pfr/detail/sequence_tuple.hpp>
  11. #include <boost/pfr/detail/rvalue_t.hpp>
  12. #include <boost/pfr/detail/size_t_.hpp>
  13. #if !defined(BOOST_PFR_INTERFACE_UNIT)
  14. #include <type_traits>
  15. #include <utility>
  16. #include <memory> // std::addressof
  17. #endif
  18. namespace boost { namespace pfr { namespace detail {
  19. // Our own implementation of std::aligned_storage. On godbolt with MSVC, I have compilation errors
  20. // using the standard version, it seems the compiler cannot generate default ctor.
  21. template<std::size_t s, std::size_t a>
  22. struct internal_aligned_storage {
  23. alignas(a) char storage_[s];
  24. };
  25. // Metafunction that replaces tuple<T1, T2, T3, ...> with
  26. // tuple<std::aligned_storage_t<sizeof(T1), alignof(T1)>, std::aligned_storage<sizeof(T2), alignof(T2)>, ...>
  27. //
  28. // The point is, the new tuple is "layout compatible" in the sense that members have the same offsets,
  29. // but this tuple is constexpr constructible.
  30. template <typename T>
  31. struct tuple_of_aligned_storage;
  32. template <typename... Ts>
  33. struct tuple_of_aligned_storage<sequence_tuple::tuple<Ts...>> {
  34. using type = sequence_tuple::tuple<internal_aligned_storage<sizeof(Ts),
  35. #if defined(__GNUC__) && __GNUC__ < 8 && !defined(__x86_64__) && !defined(__CYGWIN__)
  36. // Before GCC-8 the `alignof` was returning the optimal alignment rather than the minimal one.
  37. // We have to adjust the alignment because otherwise we get the wrong offset.
  38. (alignof(Ts) > 4 ? 4 : alignof(Ts))
  39. #else
  40. alignof(Ts)
  41. #endif
  42. >...>;
  43. };
  44. // Note: If pfr has a typelist also, could also have an overload for that here
  45. template <typename T>
  46. using tuple_of_aligned_storage_t = typename tuple_of_aligned_storage<T>::type;
  47. /***
  48. * Given a structure type and its sequence of members, we want to build a function
  49. * object "getter" that implements a version of `std::get` using offset arithmetic
  50. * and reinterpret_cast.
  51. *
  52. * typename U should be a user-defined struct
  53. * typename S should be a sequence_tuple which is layout compatible with U
  54. */
  55. template <typename U, typename S>
  56. class offset_based_getter {
  57. using this_t = offset_based_getter<U, S>;
  58. static_assert(sizeof(U) == sizeof(S), "====================> Boost.PFR: Member sequence does not indicate correct size for struct type! Maybe the user-provided type is not a SimpleAggregate?");
  59. static_assert(alignof(U) == alignof(S), "====================> Boost.PFR: Member sequence does not indicate correct alignment for struct type!");
  60. static_assert(!std::is_const<U>::value, "====================> Boost.PFR: const should be stripped from user-defined type when using offset_based_getter or overload resolution will be ambiguous later, this indicates an error within pfr");
  61. static_assert(!std::is_reference<U>::value, "====================> Boost.PFR: reference should be stripped from user-defined type when using offset_based_getter or overload resolution will be ambiguous later, this indicates an error within pfr");
  62. static_assert(!std::is_volatile<U>::value, "====================> Boost.PFR: volatile should be stripped from user-defined type when using offset_based_getter or overload resolution will be ambiguous later. this indicates an error within pfr");
  63. // Get type of idx'th member
  64. template <std::size_t idx>
  65. using index_t = typename sequence_tuple::tuple_element<idx, S>::type;
  66. // Get offset of idx'th member
  67. // Idea: Layout object has the same offsets as instance of S, so if S and U are layout compatible, then these offset
  68. // calculations are correct.
  69. template <std::size_t idx>
  70. static constexpr std::ptrdiff_t offset() noexcept {
  71. constexpr tuple_of_aligned_storage_t<S> layout{};
  72. return &sequence_tuple::get<idx>(layout).storage_[0] - &sequence_tuple::get<0>(layout).storage_[0];
  73. }
  74. // Encapsulates offset arithmetic and reinterpret_cast
  75. template <std::size_t idx>
  76. static index_t<idx> * get_pointer(U * u) noexcept {
  77. return reinterpret_cast<index_t<idx> *>(reinterpret_cast<char *>(u) + this_t::offset<idx>());
  78. }
  79. template <std::size_t idx>
  80. static const index_t<idx> * get_pointer(const U * u) noexcept {
  81. return reinterpret_cast<const index_t<idx> *>(reinterpret_cast<const char *>(u) + this_t::offset<idx>());
  82. }
  83. template <std::size_t idx>
  84. static volatile index_t<idx> * get_pointer(volatile U * u) noexcept {
  85. return reinterpret_cast<volatile index_t<idx> *>(reinterpret_cast<volatile char *>(u) + this_t::offset<idx>());
  86. }
  87. template <std::size_t idx>
  88. static const volatile index_t<idx> * get_pointer(const volatile U * u) noexcept {
  89. return reinterpret_cast<const volatile index_t<idx> *>(reinterpret_cast<const volatile char *>(u) + this_t::offset<idx>());
  90. }
  91. public:
  92. template <std::size_t idx>
  93. index_t<idx> & get(U & u, size_t_<idx>) const noexcept {
  94. return *this_t::get_pointer<idx>(std::addressof(u));
  95. }
  96. template <std::size_t idx>
  97. index_t<idx> const & get(U const & u, size_t_<idx>) const noexcept {
  98. return *this_t::get_pointer<idx>(std::addressof(u));
  99. }
  100. template <std::size_t idx>
  101. index_t<idx> volatile & get(U volatile & u, size_t_<idx>) const noexcept {
  102. return *this_t::get_pointer<idx>(std::addressof(u));
  103. }
  104. template <std::size_t idx>
  105. index_t<idx> const volatile & get(U const volatile & u, size_t_<idx>) const noexcept {
  106. return *this_t::get_pointer<idx>(std::addressof(u));
  107. }
  108. // rvalues must not be used here, to avoid template instantiation bloats.
  109. template <std::size_t idx>
  110. index_t<idx> && get(rvalue_t<U> u, size_t_<idx>) const = delete;
  111. };
  112. }}} // namespace boost::pfr::detail
  113. #endif // BOOST_PFR_DETAIL_OFFSET_LIST_HPP