2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 04:22:13 +00:00

Tweak the inheritance check to do less instantiations and to work on more compilers

This commit is contained in:
Antony Polukhin
2021-03-28 19:34:29 +03:00
parent 180db174ad
commit ea4c6e85f7
3 changed files with 83 additions and 29 deletions

View File

@@ -26,41 +26,26 @@
namespace boost { namespace pfr { namespace detail {
template <class T, class U>
constexpr void static_assert_non_inherited() noexcept {
static_assert(
std::is_same<std::remove_cv_t<U>, std::remove_cv_t<T>>::value
|| !std::is_base_of<U, T>::value,
"====================> Boost.PFR: Boost.PFR: Inherited types are not supported."
);
}
///////////////////// Structure that can be converted to reference to anything
template<class T=void>
struct ubiq_lref_constructor_ {
struct ubiq_lref_constructor {
std::size_t ignore;
template <class Type> constexpr operator Type&() const && noexcept { // tweak for template_unconstrained.cpp like cases
static_assert_non_inherited<T, Type>();
return detail::unsafe_declval<Type&>();
};
template <class Type> constexpr operator Type&() const & noexcept { // tweak for optional_chrono.cpp like cases
static_assert_non_inherited<T, Type>();
return detail::unsafe_declval<Type&>();
};
};
using ubiq_lref_constructor = ubiq_lref_constructor_<>;
///////////////////// Structure that can be converted to rvalue reference to anything
template<class T=void>
struct ubiq_rref_constructor_ {
struct ubiq_rref_constructor {
std::size_t ignore;
template <class Type> /*constexpr*/ operator Type() const && noexcept { // Allows initialization of rvalue reference fields and move-only types
static_assert_non_inherited<T, Type>();
return detail::unsafe_declval<Type>();
};
};
using ubiq_rref_constructor = ubiq_rref_constructor_<>;
#ifndef __cpp_lib_is_aggregate
///////////////////// Hand-made is_aggregate_initializable_n<T> trait
@@ -93,7 +78,7 @@ template <class T, std::size_t N>
struct is_aggregate_initializable_n {
template <std::size_t ...I>
static constexpr bool is_not_constructible_n(std::index_sequence<I...>) noexcept {
return (!std::is_constructible<T, decltype(ubiq_lref_constructor_<T>{I})...>::value && !std::is_constructible<T, decltype(ubiq_rref_constructor_<T>{I})...>::value)
return (!std::is_constructible<T, decltype(ubiq_lref_constructor{I})...>::value && !std::is_constructible<T, decltype(ubiq_rref_constructor{I})...>::value)
|| is_single_field_and_aggregate_initializable<N, T>::value
;
}
@@ -108,14 +93,68 @@ struct is_aggregate_initializable_n {
#endif // #ifndef __cpp_lib_is_aggregate
///////////////////// Detect aggregates with inheritance
template <class Derived, class U>
constexpr bool static_assert_non_inherited() noexcept {
static_assert(
!std::is_base_of<U, Derived>::value,
"====================> Boost.PFR: Boost.PFR: Inherited types are not supported."
);
return true;
}
template <class Derived>
struct ubiq_lref_base_asserting {
template <class Type> constexpr operator Type&() const && // tweak for template_unconstrained.cpp like cases
noexcept(detail::static_assert_non_inherited<Derived, Type>()) // force the computation of assert function
{
return detail::unsafe_declval<Type&>();
};
template <class Type> constexpr operator Type&() const & // tweak for optional_chrono.cpp like cases
noexcept(detail::static_assert_non_inherited<Derived, Type>()) // force the computation of assert function
{
return detail::unsafe_declval<Type&>();
};
};
template <class Derived>
struct ubiq_rref_base_asserting {
template <class Type> /*constexpr*/ operator Type() const && // Allows initialization of rvalue reference fields and move-only types
noexcept(detail::static_assert_non_inherited<Derived, Type>()) // force the computation of assert function
{
return detail::unsafe_declval<Type>();
};
};
template <class T, std::size_t I0, std::size_t... I, class /*Enable*/ = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
constexpr auto assert_first_not_base(std::index_sequence<I0, I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_lref_base_asserting<T>{}, ubiq_lref_constructor{I}... })>::type
{
return nullptr;
}
template <class T, std::size_t I0, std::size_t... I, class /*Enable*/ = typename std::enable_if<!std::is_copy_constructible<T>::value>::type>
constexpr auto assert_first_not_base(std::index_sequence<I0, I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_rref_base_asserting<T>{}, ubiq_rref_constructor{I}... })>::type
{
return nullptr;
}
template <class T>
constexpr void* assert_first_not_base(std::index_sequence<>) noexcept
{
return nullptr;
}
///////////////////// Helper for SFINAE on fields count
template <class T, std::size_t... I, class /*Enable*/ = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
constexpr auto enable_if_constructible_helper(std::index_sequence<I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_lref_constructor_<T>{I}... })>::type;
-> typename std::add_pointer<decltype(T{ ubiq_lref_constructor{I}... })>::type;
template <class T, std::size_t... I, class /*Enable*/ = typename std::enable_if<!std::is_copy_constructible<T>::value>::type>
constexpr auto enable_if_constructible_helper(std::index_sequence<I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_rref_constructor_<T>{I}... })>::type;
-> typename std::add_pointer<decltype(T{ ubiq_rref_constructor{I}... })>::type;
template <class T, std::size_t N, class /*Enable*/ = decltype( enable_if_constructible_helper<T>(detail::make_index_sequence<N>()) ) >
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<type>(size_t_<max_fields_count>{}, 1L, 1L);
detail::assert_first_not_base<type>(detail::make_index_sequence<result>{});
#ifndef __cpp_lib_is_aggregate
static_assert(
is_aggregate_initializable_n<type, result>::value,

View File

@@ -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 <boost/pfr/core.hpp>
#include <boost/core/lightweight_test.hpp>
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<B>::value; // Must be a compile time error
return boost::report_errors();
}

View File

@@ -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 <boost/pfr/core.hpp>
struct A {
int zero;
};
struct B : public A {
int one;
int two;
};
int main() {
(void)boost::pfr::tuple_size<B>::value; // Must be a compile time error
}