// Copyright (c) 2017-2018 Chris Beck // Copyright (c) 2019-2021 Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_PFR_DETAIL_OFFSET_BASED_GETTER_HPP #define BOOST_PFR_DETAIL_OFFSET_BASED_GETTER_HPP #pragma once #include #include #include #include #include #include namespace boost { namespace pfr { namespace detail { // Our own implementation of std::aligned_storage. On godbolt with MSVC, I have compilation errors // using the standard version, it seems the compiler cannot generate default ctor. template struct internal_aligned_storage { alignas(a) char storage_[s]; }; // Metafunction that replaces tuple with // tuple, std::aligned_storage, ...> // // The point is, the new tuple is "layout compatible" in the sense that members have the same offsets, // but this tuple is constexpr constructible. template struct tuple_of_aligned_storage; template struct tuple_of_aligned_storage> { using type = sequence_tuple::tuple 4 ? 4 : alignof(Ts)) #else alignof(Ts) #endif >...>; }; // Note: If pfr has a typelist also, could also have an overload for that here template using tuple_of_aligned_storage_t = typename tuple_of_aligned_storage::type; /*** * Given a structure type and its sequence of members, we want to build a function * object "getter" that implements a version of `std::get` using offset arithmetic * and reinterpret_cast. * * typename U should be a user-defined struct * typename S should be a sequence_tuple which is layout compatible with U */ template class offset_based_getter { using this_t = offset_based_getter; 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?"); static_assert(alignof(U) == alignof(S), "====================> Boost.PFR: Member sequence does not indicate correct alignment for struct type!"); static_assert(!std::is_const::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"); static_assert(!std::is_reference::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"); static_assert(!std::is_volatile::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"); // Get type of idx'th member template using index_t = typename sequence_tuple::tuple_element::type; // Get offset of idx'th member // Idea: Layout object has the same offsets as instance of S, so if S and U are layout compatible, then these offset // calculations are correct. template static constexpr std::ptrdiff_t offset() noexcept { constexpr tuple_of_aligned_storage_t layout{}; return &sequence_tuple::get(layout).storage_[0] - &sequence_tuple::get<0>(layout).storage_[0]; } // Encapsulates offset arithmetic and reinterpret_cast template static index_t * get_pointer(U * u) noexcept { return reinterpret_cast *>(reinterpret_cast(u) + this_t::offset()); } template static const index_t * get_pointer(const U * u) noexcept { return reinterpret_cast *>(reinterpret_cast(u) + this_t::offset()); } template static volatile index_t * get_pointer(volatile U * u) noexcept { return reinterpret_cast *>(reinterpret_cast(u) + this_t::offset()); } template static const volatile index_t * get_pointer(const volatile U * u) noexcept { return reinterpret_cast *>(reinterpret_cast(u) + this_t::offset()); } public: template index_t & get(U & u, size_t_) const noexcept { return *this_t::get_pointer(std::addressof(u)); } template index_t const & get(U const & u, size_t_) const noexcept { return *this_t::get_pointer(std::addressof(u)); } template index_t volatile & get(U volatile & u, size_t_) const noexcept { return *this_t::get_pointer(std::addressof(u)); } template index_t const volatile & get(U const volatile & u, size_t_) const noexcept { return *this_t::get_pointer(std::addressof(u)); } // rvalues must not be used here, to avoid template instantiation bloats. template index_t && get(rvalue_t u, size_t_) const = delete; }; }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_OFFSET_LIST_HPP