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