diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 5348e4d..f250bb5 100644 --- a/include/boost/pfr/detail/fields_count.hpp +++ b/include/boost/pfr/detail/fields_count.hpp @@ -26,41 +26,26 @@ namespace boost { namespace pfr { namespace detail { -template -constexpr void static_assert_non_inherited() noexcept { - static_assert( - std::is_same, std::remove_cv_t>::value - || !std::is_base_of::value, - "====================> Boost.PFR: Boost.PFR: Inherited types are not supported." - ); -} - ///////////////////// Structure that can be converted to reference to anything -template -struct ubiq_lref_constructor_ { +struct ubiq_lref_constructor { std::size_t ignore; template constexpr operator Type&() const && noexcept { // tweak for template_unconstrained.cpp like cases - static_assert_non_inherited(); return detail::unsafe_declval(); }; template constexpr operator Type&() const & noexcept { // tweak for optional_chrono.cpp like cases - static_assert_non_inherited(); return detail::unsafe_declval(); }; }; -using ubiq_lref_constructor = ubiq_lref_constructor_<>; ///////////////////// Structure that can be converted to rvalue reference to anything -template -struct ubiq_rref_constructor_ { +struct ubiq_rref_constructor { std::size_t ignore; template /*constexpr*/ operator Type() const && noexcept { // Allows initialization of rvalue reference fields and move-only types - static_assert_non_inherited(); return detail::unsafe_declval(); }; }; -using ubiq_rref_constructor = ubiq_rref_constructor_<>; + #ifndef __cpp_lib_is_aggregate ///////////////////// Hand-made is_aggregate_initializable_n trait @@ -93,7 +78,7 @@ template struct is_aggregate_initializable_n { template static constexpr bool is_not_constructible_n(std::index_sequence) noexcept { - return (!std::is_constructible{I})...>::value && !std::is_constructible{I})...>::value) + return (!std::is_constructible::value && !std::is_constructible::value) || is_single_field_and_aggregate_initializable::value ; } @@ -108,14 +93,68 @@ struct is_aggregate_initializable_n { #endif // #ifndef __cpp_lib_is_aggregate +///////////////////// Detect aggregates with inheritance +template +constexpr bool static_assert_non_inherited() noexcept { + static_assert( + !std::is_base_of::value, + "====================> Boost.PFR: Boost.PFR: Inherited types are not supported." + ); + return true; +} + +template +struct ubiq_lref_base_asserting { + template constexpr operator Type&() const && // tweak for template_unconstrained.cpp like cases + noexcept(detail::static_assert_non_inherited()) // force the computation of assert function + { + return detail::unsafe_declval(); + }; + + template constexpr operator Type&() const & // tweak for optional_chrono.cpp like cases + noexcept(detail::static_assert_non_inherited()) // force the computation of assert function + { + return detail::unsafe_declval(); + }; +}; + +template +struct ubiq_rref_base_asserting { + template /*constexpr*/ operator Type() const && // Allows initialization of rvalue reference fields and move-only types + noexcept(detail::static_assert_non_inherited()) // force the computation of assert function + { + return detail::unsafe_declval(); + }; +}; + +template ::value>::type> +constexpr auto assert_first_not_base(std::index_sequence) noexcept + -> typename std::add_pointer{}, ubiq_lref_constructor{I}... })>::type +{ + return nullptr; +} + +template ::value>::type> +constexpr auto assert_first_not_base(std::index_sequence) noexcept + -> typename std::add_pointer{}, ubiq_rref_constructor{I}... })>::type +{ + return nullptr; +} + +template +constexpr void* assert_first_not_base(std::index_sequence<>) noexcept +{ + return nullptr; +} + ///////////////////// Helper for SFINAE on fields count template ::value>::type> constexpr auto enable_if_constructible_helper(std::index_sequence) noexcept - -> typename std::add_pointer{I}... })>::type; + -> typename std::add_pointer::type; template ::value>::type> constexpr auto enable_if_constructible_helper(std::index_sequence) noexcept - -> typename std::add_pointer{I}... })>::type; + -> typename std::add_pointer::type; template (detail::make_index_sequence()) ) > using enable_if_constructible_helper_t = std::size_t; @@ -259,6 +298,8 @@ constexpr std::size_t fields_count() noexcept { constexpr std::size_t max_fields_count = (sizeof(type) * CHAR_BIT); // We multiply by CHAR_BIT because the type may have bitfields in T constexpr std::size_t result = detail::detect_fields_count_dispatch(size_t_{}, 1L, 1L); + detail::assert_first_not_base(detail::make_index_sequence{}); + #ifndef __cpp_lib_is_aggregate static_assert( is_aggregate_initializable_n::value, diff --git a/test/compile-fail/inherited.cpp b/test/compile-fail/inherited.cpp index a867c08..9725331 100644 --- a/test/compile-fail/inherited.cpp +++ b/test/compile-fail/inherited.cpp @@ -1,10 +1,10 @@ // Copyright (c) 2021 Denis Mikhailov +// Copyright (c) 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) #include -#include struct A {}; @@ -16,13 +16,6 @@ struct B : A }; int main() { -#ifndef __cpp_lib_is_aggregate -// TODO: No known way to detect inherited -# error No known way to detect inherited. -#endif (void)boost::pfr::tuple_size::value; // Must be a compile time error - return boost::report_errors(); } - - diff --git a/test/compile-fail/inherited_nonempty.cpp b/test/compile-fail/inherited_nonempty.cpp new file mode 100644 index 0000000..c9bf201 --- /dev/null +++ b/test/compile-fail/inherited_nonempty.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 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) + +#include + +struct A { + int zero; +}; + +struct B : public A { + int one; + int two; +}; + +int main() { + (void)boost::pfr::tuple_size::value; // Must be a compile time error +} +