// Copyright (c) 2017-2018 Alexandr Poltavsky, Antony Polukhin. // Copyright (c) 2019-2024 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) // The Great Type Loophole (C++14) // Initial implementation by Alexandr Poltavsky, http://alexpolt.github.io // // Description: // The Great Type Loophole is a technique that allows to exchange type information with template // instantiations. Basically you can assign and read type information during compile time. // Here it is used to detect data members of a data type. I described it for the first time in // this blog post http://alexpolt.github.io/type-loophole.html . // // This technique exploits the http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 // CWG 2118. Stateful metaprogramming via friend injection // Note: CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined. #ifndef BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP #define BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP #pragma once #include #include #include #include #include #include #include #include #include #include #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wmissing-braces" # pragma clang diagnostic ignored "-Wundefined-inline" # pragma clang diagnostic ignored "-Wundefined-internal" # pragma clang diagnostic ignored "-Wmissing-field-initializers" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wnon-template-friend" #endif namespace boost { namespace pfr { namespace detail { // tag generates friend declarations and helps with overload resolution. // There are two types: one with the auto return type, which is the way we read types later. // The second one is used in the detection of instantiations without which we'd get multiple // definitions. template struct tag { friend auto loophole(tag); }; // The definitions of friend functions. template struct fn_def_lref { friend auto loophole(tag) { // Standard Library containers do not SFINAE on invalid copy constructor. Because of that std::vector> reports that it is copyable, // which leads to an instantiation error at this place. // // To workaround the issue, we check that the type U is movable, and move it in that case. using no_extents_t = std::remove_all_extents_t; return static_cast< std::conditional_t::value, no_extents_t&&, no_extents_t&> >( boost::pfr::detail::unsafe_declval() ); } }; template struct fn_def_rref { friend auto loophole(tag) { return std::move(boost::pfr::detail::unsafe_declval< std::remove_all_extents_t& >()); } }; // Those specializations are to avoid multiple definition errors. template struct fn_def_lref {}; template struct fn_def_rref {}; // This has a templated conversion operator which in turn triggers instantiations. // Important point, using sizeof seems to be more reliable. Also default template // arguments are "cached" (I think). To fix that I provide a U template parameter to // the ins functions which do the detection using constexpr friend functions and SFINAE. template struct loophole_ubiq_lref { template static std::size_t ins(...); template{})) > static char ins(int); template(0)) == sizeof(char)>)> constexpr operator U&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior. }; template struct loophole_ubiq_rref { template static std::size_t ins(...); template{})) > static char ins(int); template(0)) == sizeof(char)>)> constexpr operator U&&() const&& noexcept; // `const&&` here helps to avoid ambiguity in loophole instantiations. optional_like test validate that behavior. }; // This is a helper to turn a data structure into a tuple. template struct loophole_type_list_lref; template struct loophole_type_list_lref< T, std::index_sequence > // Instantiating loopholes: : sequence_tuple::tuple< decltype(T{ loophole_ubiq_lref{}... }, 0) > { using type = sequence_tuple::tuple< decltype(loophole(tag{}))... >; }; template struct loophole_type_list_rref; template struct loophole_type_list_rref< T, std::index_sequence > // Instantiating loopholes: : sequence_tuple::tuple< decltype(T{ loophole_ubiq_rref{}... }, 0) > { using type = sequence_tuple::tuple< decltype(loophole(tag{}))... >; }; // Lazily returns loophole_type_list_{lr}ref. template struct loophole_type_list_selector { using type = loophole_type_list_lref; }; template struct loophole_type_list_selector { using type = loophole_type_list_rref; }; template auto tie_as_tuple_loophole_impl(T& lvalue) noexcept { using type = std::remove_cv_t>; using indexes = detail::make_index_sequence()>; using loophole_type_list = typename detail::loophole_type_list_selector< std::is_copy_constructible>::value, type, indexes >::type; using tuple_type = typename loophole_type_list::type; return boost::pfr::detail::make_flat_tuple_of_references( lvalue, offset_based_getter{}, size_t_<0>{}, size_t_{} ); } template auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); return boost::pfr::detail::tie_as_tuple_loophole_impl( val ); } template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); std::forward(f)( boost::pfr::detail::tie_as_tuple_loophole_impl(t) ); } }}} // namespace boost::pfr::detail #ifdef __clang__ # pragma clang diagnostic pop #elif defined(__GNUC__) # pragma GCC diagnostic pop #endif #endif // BOOST_PFR_DETAIL_CORE14_LOOPHOLE_HPP