1
0
forked from Alepha/Alepha

Fixed tagged ctor argument inspection code.

This commit is contained in:
2024-03-21 15:31:02 -04:00
parent 3707ec5cc2
commit 1e3b3707e2
4 changed files with 283 additions and 0 deletions

View File

@ -1,3 +1,4 @@
add_subdirectory( tuplizeAggregate.test )
add_subdirectory( has_tagged_ctor.test )
add_subdirectory( tagged_ctor_count.test )
add_subdirectory( tagged_ctor_arg.test )

View File

@ -0,0 +1,234 @@
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <tuple>
#include <utility>
#include <type_traits>
#include <Alepha/Concepts.h>
#include <Alepha/Reflection/detail/config.h>
#include <Alepha/Reflection/has_tagged_ctor.h>
#include <Alepha/Reflection/tagged_ctor_count.h>
namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_arg_m
{
inline namespace exports {}
namespace C
{
using namespace Reflection::detail::config_m::C;
}
namespace protection
{
//template< typename Fake, typename Dummy >
//inline void get_argument_type( Fake, Dummy ) {}
template< typename Type, typename tag, std::size_t index >
struct lookup_helper
{
template< typename Dummy, typename >
friend constexpr auto get_argument_type( const lookup_helper< Type, tag, index > &, Dummy );
};
template< typename Type, typename tag, std::size_t index, typename arg_type >
struct argument_extractor
{
template< typename Dummy, typename= std::enable_if_t< std::is_same_v< Dummy, std::nullptr_t > > >
friend constexpr auto
get_argument_type( const lookup_helper< Type, tag, index > &, Dummy )
{
return std::type_identity< arg_type >();
}
static constexpr void member() {}
};
template< typename Type, typename tag, std::size_t index >
struct exporting_argument
{
template
<
typename ArgType,
auto= argument_extractor< Type, tag, index, std::decay_t< ArgType > >::member//,
//typename= std::enable_if_t< not std::is_same_v< ArgType, Type > >
>
constexpr operator ArgType () const;
};
template< typename Forbidden >
struct argument
{
template< typename T, typename= std::enable_if_t< not std::is_same_v< T, Forbidden > > >
constexpr operator T ();
};
template< typename T, typename ... Args, typename= std::enable_if_t< std::is_constructible_v< T, Args... > > >
constexpr void construct( std::tuple< Args... > && );
template
<
typename T,
typename tag,
typename sequence= std::index_sequence<>,
typename= void,
typename= void
>
struct extract_all_ctor_arguments
: extract_all_ctor_arguments< T, tag, std::make_index_sequence< sequence::size() + 1 > > {};
template
<
typename T,
typename tag
>
struct extract_all_ctor_arguments< T, tag, std::make_index_sequence< C::max_ctor_size >, void >
{
struct impossible;
static_assert( std::is_same_v< impossible, T >, "Max recursion reached." );
};
template
<
typename T,
typename tag,
std::size_t ... positions
>
struct extract_all_ctor_arguments
<
T,
tag,
std::index_sequence< positions... >,
std::void_t
<
decltype
(
construct< T >( std::tuple_cat
(
std::make_tuple( ( positions, std::declval< argument< T > >() )... ),
std::make_tuple( std::declval< tag >() )
) )
)
>
>
{
template
<
typename int_const,
typename= std::void_t
<
decltype
(
construct< T >( std::tuple_cat
(
std::make_tuple( ( positions, std::declval< exporting_argument< T, tag, positions > >() )... ),
std::make_tuple( std::declval< tag >() )
) )
)
>
>
struct constant_wrapper
{
// Replay the construction in this body, to cause the friend injection of our
// exporters for detected params to happen only once. If we didn't match, we don't
// want to create false overloads. The standard is murky on which return type
// dominates, in those cases. We are abusing something CWG doesn't like, but they've
// had over a decade to fix it... and some future standard will provide real reflection
// and this hack can vanish.
using type= std::conditional_t
<
true,
typename int_const::type,
// Force instantiation of tag extractor:
decltype( argument_extractor< T, tag, sizeof...( positions ), tag >::member() )
>;
};
using type= typename constant_wrapper< std::integral_constant< std::size_t, 1 + sizeof...( positions ) > >::type;
static constexpr std::size_t value= type::value;
};
template
<
typename T,
typename tag,
std::size_t index
>
struct tagged_ctor_extract_impl
{
using type= typename decltype( get_argument_type(
std::declval< lookup_helper< T, tag, index > >(), nullptr ) )::type;
};
template
<
typename T,
typename tag,
std::size_t index,
auto fake_type= extract_all_ctor_arguments< T, tag >::value
>
struct tagged_ctor_extract
{
using type= typename tagged_ctor_extract_impl< T, tag, index >::type;
};
}
namespace impl
{
template< typename T, typename tag, std::size_t index >
struct tagged_ctor_arg
{
using type= std::decay_t< typename protection::tagged_ctor_extract< T, tag, index >::type >;
};
template< typename T, typename tag, std::size_t index >
using tagged_ctor_arg_t= typename tagged_ctor_arg< T, tag, index >::type;
}
namespace build_impl
{
template< typename T, typename tag, std::size_t size, std::size_t index= 0 >
struct build_ctor_tuple
{
using type= decltype
(
std::tuple_cat
(
std::declval< std::tuple< impl::tagged_ctor_arg_t< T, tag, index > > >(),
std::declval< typename build_ctor_tuple< T, tag, size, index + 1 >::type >()
)
);
};
template< typename T, typename tag, std::size_t size >
struct build_ctor_tuple< T, tag, size, size >
{
using type= std::tuple<>;
};
template< typename T, typename tag >
struct build_ctor_tuple_wrapper
{
static_assert( has_tagged_ctor_v< T, tag > );
inline static constexpr auto sz= tagged_ctor_count_v< T, tag >;
using type= typename build_ctor_tuple< T, tag, sz >::type;
};
}
namespace exports
{
template< typename T, typename tag >
using tagged_ctor_args_t= typename build_impl::build_ctor_tuple_wrapper< T, tag >::type;
}
}
namespace Alepha::Hydrogen::Reflection::inline exports::inline tagged_ctor_arg_m
{
using namespace detail::tagged_ctor_arg_m::exports;
}

View File

@ -0,0 +1,47 @@
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Reflection/tagged_ctor_arg.h>
#include <type_traits>
namespace
{
struct tag {};
struct tag2 {};
struct fake {};
struct instance
{
// There are arity issues with the tagged ctor lookup.
// No other ctor can have the same arity as a tagged ctor.
// (Each tagged ctor has to have a unique arity.)
//
// If they do, it causes "redefinition errors" from template
// friend injection. I believe that the reason for this is
// that each `exporting_argument` object winds up generating
// several `operator T` overloads, one for each object ctor
// that it attempts to convert to. Thus we wind up with
// duplicate bindings. While this is annoying, it's not
// significantly different to the `function_traits` inspector
// in that it requires a single overload. If duplicate
// arity overloads are needed, padding dummy arguments can be
// used are needed. These dummies cannot be defaulted.
// Further, the tag arguments should not be defaulted, as that
// creates pseudo-overloads with the same arity. This advice
// not to default tags applies only when dealing with
// overloads.
//
// The hope is that in a future version of C++ there will
// be a true reflection mechanism for ctors -- and thus that
// facility can be used as a replacement for this reflection
// hack.
explicit instance( int a, float b, char c, double d, tag t );
explicit instance( long, long, long, long, fake, tag2 t );
};
using namespace Alepha::Reflection::exports::tagged_ctor_arg_m;
using tuple= tagged_ctor_args_t< instance, tag >;
std::tuple< int, float, char, double, tag > t= tuple{};
static_assert( std::is_same_v< tuple, std::tuple< int, float, char, double, tag > > );
using tuple2= tagged_ctor_args_t< instance, tag2 >;
static_assert( std::is_same_v< tuple2, std::tuple< long, long, long, long, fake, tag2 > > );
}

View File

@ -0,0 +1 @@
unit_test( 0 )