forked from Alepha/Alepha
Apply the newer namespace rules and layout/formatting.
This commit is contained in:
@ -16,176 +16,171 @@ static_assert( __cplusplus > 2020'99 );
|
||||
#include <Alepha/Meta/overload.h>
|
||||
#include <Alepha/Meta/type_value.h>
|
||||
|
||||
namespace Alepha::Hydrogen::Reflection
|
||||
namespace Alepha::Hydrogen::Reflection ::detail:: aggregate_members_m
|
||||
{
|
||||
inline namespace exports { inline namespace aggregate_members {} }
|
||||
inline namespace exports {}
|
||||
|
||||
namespace detail::aggregate_members
|
||||
using Meta::overload;
|
||||
|
||||
/*!
|
||||
* Basic methodology here.
|
||||
*
|
||||
* The number of members in an aggregate is equal to the number of initializer parameters it takes less
|
||||
* the number of empty base classes it has. In simple terms, this would be `init_terms< T > - empty_bases< T >`,
|
||||
* However, it's not that simple. To do that the easy way, one might need get `std::tuple< InitTerms... >` and then compute
|
||||
* which terms were bases. Now that gets a bit complicated, as in C++ one can't just directly get a tuple of initializer
|
||||
* arguments (portably) to scoop out the arguments and analyze them one-by-one. One can, however, constrain the arguments
|
||||
* one-by-one in templates. Those constraints cannot directly leak out the types they conclude, as that requires side
|
||||
* effects. (Yes, template-friend-injection can be used here, but these mechanisms are extremely delicate. They're
|
||||
* not really portable. Further, the way that constraints get instantiated for matching is prone to complications.)
|
||||
*
|
||||
* Instead, a side-stepping approach is required. It's trivial to ask: "Can this object be constructed from these
|
||||
* N adaptive types, where the first one is constrained to be a base class of your object's type?" If yes, then
|
||||
* this proves a (likely) empty base. One can just recursively iterate through more an more constrained adaptive
|
||||
* types until the first non-base type is reached. At this point, there are no more than that many base classes.
|
||||
*
|
||||
* There may actually be fewer base classes, however. Consider:
|
||||
*
|
||||
* ```
|
||||
* struct SneakyBase {};
|
||||
*
|
||||
* struct Complicated : SneakyBase
|
||||
* {
|
||||
* SneakyBase member;
|
||||
* };
|
||||
* ```
|
||||
* In that case, a constrained adaptable argument would see two base types. Here is where a bit of C++ trivia and
|
||||
* knowledge comes into play. C++ forbids repetition of a base class's type. Therefore the sequence of base classes
|
||||
* cannot have repeats. The solution is to perform a nested exploration of instantiations of `checker` types which
|
||||
* has each descent disable casting to `std::is_base_of_v` types which already have been expanded. Thus whatever
|
||||
* count this expands to, it will be the correct empty-bases count. Then that count can be subtracted from the
|
||||
* initializer list count.
|
||||
*
|
||||
* Note that this will not work with types that have non-empty bases, but those types cannot be decomposed,
|
||||
* anyhow. Such types cannot have C++17 reflection performed on them.
|
||||
*
|
||||
* For the moment, computing a deep-dive on constrainted adaptable arguments is skipped. It's a lot more
|
||||
* complicated than just counting empty bases. As long as the first actual member is not also a base class,
|
||||
* this technique will work.
|
||||
*/
|
||||
|
||||
// The basic adaptable argument. Because it pretends to be anything, it can be used as a parameter in invoking
|
||||
// any initialization method.
|
||||
struct argument { template< typename T > constexpr operator T (); };
|
||||
|
||||
// Any empty-base-class argument.
|
||||
template< typename Aggregate >
|
||||
struct empty_base
|
||||
{
|
||||
inline namespace exports {}
|
||||
|
||||
using Meta::overload;
|
||||
|
||||
/*!
|
||||
* Basic methodology here.
|
||||
*
|
||||
* The number of members in an aggregate is equal to the number of initializer parameters it takes less
|
||||
* the number of empty base classes it has. In simple terms, this would be `init_terms< T > - empty_bases< T >`,
|
||||
* However, it's not that simple. To do that the easy way, one might need get `std::tuple< InitTerms... >` and then compute
|
||||
* which terms were bases. Now that gets a bit complicated, as in C++ one can't just directly get a tuple of initializer
|
||||
* arguments (portably) to scoop out the arguments and analyze them one-by-one. One can, however, constrain the arguments
|
||||
* one-by-one in templates. Those constraints cannot directly leak out the types they conclude, as that requires side
|
||||
* effects. (Yes, template-friend-injection can be used here, but these mechanisms are extremely delicate. They're
|
||||
* not really portable. Further, the way that constraints get instantiated for matching is prone to complications.)
|
||||
*
|
||||
* Instead, a side-stepping approach is required. It's trivial to ask: "Can this object be constructed from these
|
||||
* N adaptive types, where the first one is constrained to be a base class of your object's type?" If yes, then
|
||||
* this proves a (likely) empty base. One can just recursively iterate through more an more constrained adaptive
|
||||
* types until the first non-base type is reached. At this point, there are no more than that many base classes.
|
||||
*
|
||||
* There may actually be fewer base classes, however. Consider:
|
||||
*
|
||||
* ```
|
||||
* struct SneakyBase {};
|
||||
*
|
||||
* struct Complicated : SneakyBase
|
||||
* {
|
||||
* SneakyBase member;
|
||||
* };
|
||||
* ```
|
||||
* In that case, a constrained adaptable argument would see two base types. Here is where a bit of C++ trivia and
|
||||
* knowledge comes into play. C++ forbids repetition of a base class's type. Therefore the sequence of base classes
|
||||
* cannot have repeats. The solution is to perform a nested exploration of instantiations of `checker` types which
|
||||
* has each descent disable casting to `std::is_base_of_v` types which already have been expanded. Thus whatever
|
||||
* count this expands to, it will be the correct empty-bases count. Then that count can be subtracted from the
|
||||
* initializer list count.
|
||||
*
|
||||
* Note that this will not work with types that have non-empty bases, but those types cannot be decomposed,
|
||||
* anyhow. Such types cannot have C++17 reflection performed on them.
|
||||
*
|
||||
* For the moment, computing a deep-dive on constrainted adaptable arguments is skipped. It's a lot more
|
||||
* complicated than just counting empty bases. As long as the first actual member is not also a base class,
|
||||
* this technique will work.
|
||||
*/
|
||||
|
||||
// The basic adaptable argument. Because it pretends to be anything, it can be used as a parameter in invoking
|
||||
// any initialization method.
|
||||
struct argument { template< typename T > constexpr operator T (); };
|
||||
|
||||
// Any empty-base-class argument.
|
||||
template< typename Aggregate >
|
||||
struct empty_base
|
||||
{
|
||||
template< typename T >
|
||||
requires
|
||||
(
|
||||
true
|
||||
and EmptyType< std::decay_t< T > >
|
||||
and not SameAs< std::decay_t< T >, Aggregate >
|
||||
and DerivedFrom< Aggregate, std::decay_t< T > >
|
||||
)
|
||||
constexpr operator T ();
|
||||
|
||||
//template< typename T > constexpr operator T ()= delete;
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
constexpr bool is_empty_base_v= false;
|
||||
requires
|
||||
(
|
||||
true
|
||||
and EmptyType< std::decay_t< T > >
|
||||
and not SameAs< std::decay_t< T >, Aggregate >
|
||||
and DerivedFrom< Aggregate, std::decay_t< T > >
|
||||
)
|
||||
constexpr operator T ();
|
||||
|
||||
template< typename T >
|
||||
constexpr bool is_empty_base_v< empty_base< T > >{ true };
|
||||
//template< typename T > constexpr operator T ()= delete;
|
||||
};
|
||||
|
||||
template< typename Tuple, std::size_t baseCount, std::size_t totalCount >
|
||||
constexpr void
|
||||
check_tuple()
|
||||
template< typename T >
|
||||
constexpr bool is_empty_base_v= false;
|
||||
|
||||
template< typename T >
|
||||
constexpr bool is_empty_base_v< empty_base< T > >{ true };
|
||||
|
||||
template< typename Tuple, std::size_t baseCount, std::size_t totalCount >
|
||||
constexpr void
|
||||
check_tuple()
|
||||
{
|
||||
static_assert( std::tuple_size_v< Tuple > == totalCount );
|
||||
}
|
||||
|
||||
template< typename Aggregate, std::size_t bases, std::size_t total >
|
||||
constexpr auto
|
||||
build_init_tuple()
|
||||
{
|
||||
static_assert( bases <= total );
|
||||
if constexpr( total == 0 ) return std::tuple{};
|
||||
else if constexpr( bases > 0 )
|
||||
{
|
||||
static_assert( std::tuple_size_v< Tuple > == totalCount );
|
||||
auto result= std::tuple_cat( std::tuple{ empty_base< Aggregate >{} }, build_init_tuple< Aggregate, bases - 1, total - 1 >() );
|
||||
check_tuple< decltype( result ), bases, total >();
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename Aggregate, std::size_t bases, std::size_t total >
|
||||
constexpr auto
|
||||
build_init_tuple()
|
||||
else
|
||||
{
|
||||
static_assert( bases <= total );
|
||||
if constexpr( total == 0 ) return std::tuple{};
|
||||
else if constexpr( bases > 0 )
|
||||
{
|
||||
auto result= std::tuple_cat( std::tuple{ empty_base< Aggregate >{} }, build_init_tuple< Aggregate, bases - 1, total - 1 >() );
|
||||
check_tuple< decltype( result ), bases, total >();
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( bases == 0 );
|
||||
auto result= std::tuple_cat( std::tuple{ argument{} }, build_init_tuple< Aggregate, 0, total - 1 >() );
|
||||
check_tuple< decltype( result ), bases, total >();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template< typename T, typename Tuple >
|
||||
constexpr bool is_constructible_from_tuple_v= false;
|
||||
|
||||
template< typename T, typename ... TupleArgs >
|
||||
constexpr bool is_constructible_from_tuple_v< T, std::tuple< TupleArgs... > >
|
||||
{
|
||||
ConstructibleFrom< T, TupleArgs... >
|
||||
};
|
||||
|
||||
template< Aggregate T, typename InitTuple, std::size_t index= 0 >
|
||||
constexpr auto
|
||||
build_base_tuple()
|
||||
{
|
||||
constexpr auto init_size= aggregate_initializer_size_v< T >;
|
||||
|
||||
using DeeperTuple= decltype( build_init_tuple< T, index, init_size >() );
|
||||
|
||||
if constexpr( is_constructible_from_tuple_v< T, DeeperTuple > )
|
||||
{
|
||||
return build_base_tuple< T, DeeperTuple, index + 1 >();
|
||||
}
|
||||
else return Meta::type_value< InitTuple >{};
|
||||
}
|
||||
|
||||
template< typename ... Args, typename First >
|
||||
constexpr std::size_t
|
||||
count_empty_bases( Meta::type_value< std::tuple< First, Args... > > )
|
||||
{
|
||||
if constexpr( is_empty_base_v< First > ) return 1 + count_empty_bases( Meta::type_value< std::tuple< Args... > >{} );
|
||||
else return 0;
|
||||
}
|
||||
|
||||
constexpr std::size_t
|
||||
count_empty_bases( Meta::type_value< std::tuple<> > )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template< Aggregate T, std::size_t index= 0 >
|
||||
constexpr std::size_t
|
||||
count_empty_bases()
|
||||
{
|
||||
return count_empty_bases( build_base_tuple< T, decltype( build_init_tuple< T, 0, aggregate_initializer_size_v< T > > ) >() );
|
||||
}
|
||||
|
||||
namespace exports
|
||||
{
|
||||
template< typename T >
|
||||
struct aggregate_empty_bases : std::integral_constant< std::size_t, count_empty_bases< T >() > {};
|
||||
|
||||
template< typename T >
|
||||
constexpr std::size_t aggregate_empty_bases_v= aggregate_empty_bases< T >::value;
|
||||
|
||||
template< typename T >
|
||||
constexpr std::size_t aggregate_member_count_v= aggregate_initializer_size_v< T > - aggregate_empty_bases_v< T >;
|
||||
|
||||
template< typename T >
|
||||
struct aggregate_member_count : std::integral_constant< std::size_t, aggregate_member_count_v< T > > {};
|
||||
static_assert( bases == 0 );
|
||||
auto result= std::tuple_cat( std::tuple{ argument{} }, build_init_tuple< Aggregate, 0, total - 1 >() );
|
||||
check_tuple< decltype( result ), bases, total >();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace exports::aggregate_members
|
||||
template< typename T, typename Tuple >
|
||||
constexpr bool is_constructible_from_tuple_v= false;
|
||||
|
||||
template< typename T, typename ... TupleArgs >
|
||||
constexpr bool is_constructible_from_tuple_v< T, std::tuple< TupleArgs... > >
|
||||
{
|
||||
using namespace detail::aggregate_members::exports;
|
||||
ConstructibleFrom< T, TupleArgs... >
|
||||
};
|
||||
|
||||
template< Aggregate T, typename InitTuple, std::size_t index= 0 >
|
||||
constexpr auto
|
||||
build_base_tuple()
|
||||
{
|
||||
constexpr auto init_size= aggregate_initializer_size_v< T >;
|
||||
|
||||
using DeeperTuple= decltype( build_init_tuple< T, index, init_size >() );
|
||||
|
||||
if constexpr( is_constructible_from_tuple_v< T, DeeperTuple > )
|
||||
{
|
||||
return build_base_tuple< T, DeeperTuple, index + 1 >();
|
||||
}
|
||||
else return Meta::type_value< InitTuple >{};
|
||||
}
|
||||
|
||||
template< typename ... Args, typename First >
|
||||
constexpr std::size_t
|
||||
count_empty_bases( Meta::type_value< std::tuple< First, Args... > > )
|
||||
{
|
||||
if constexpr( is_empty_base_v< First > ) return 1 + count_empty_bases( Meta::type_value< std::tuple< Args... > >{} );
|
||||
else return 0;
|
||||
}
|
||||
|
||||
constexpr std::size_t
|
||||
count_empty_bases( Meta::type_value< std::tuple<> > )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template< Aggregate T, std::size_t index= 0 >
|
||||
constexpr std::size_t
|
||||
count_empty_bases()
|
||||
{
|
||||
return count_empty_bases( build_base_tuple< T, decltype( build_init_tuple< T, 0, aggregate_initializer_size_v< T > > ) >() );
|
||||
}
|
||||
|
||||
namespace exports
|
||||
{
|
||||
template< typename T >
|
||||
struct aggregate_empty_bases : std::integral_constant< std::size_t, count_empty_bases< T >() > {};
|
||||
|
||||
template< typename T >
|
||||
constexpr std::size_t aggregate_empty_bases_v= aggregate_empty_bases< T >::value;
|
||||
|
||||
template< typename T >
|
||||
constexpr std::size_t aggregate_member_count_v= aggregate_initializer_size_v< T > - aggregate_empty_bases_v< T >;
|
||||
|
||||
template< typename T >
|
||||
struct aggregate_member_count : std::integral_constant< std::size_t, aggregate_member_count_v< T > > {};
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Hydrogen::Reflection::inline exports::inline aggregate_members_m
|
||||
{
|
||||
using namespace detail::aggregate_members_m::exports;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user