From 696e1b56f66ac0a36480cfccf96e295f384f703b Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 18 Mar 2024 15:49:23 -0400 Subject: [PATCH 01/10] Add tagged ctor check support. --- Reflection/CMakeLists.txt | 1 + Reflection/detail/config.h | 9 ++ Reflection/tagged_ctor_size.h | 95 +++++++++++++++++++ Reflection/tagged_ctor_size.test/0.cc | 23 +++++ .../tagged_ctor_size.test/CMakeLists.txt | 1 + 5 files changed, 129 insertions(+) create mode 100644 Reflection/tagged_ctor_size.h create mode 100644 Reflection/tagged_ctor_size.test/0.cc create mode 100644 Reflection/tagged_ctor_size.test/CMakeLists.txt diff --git a/Reflection/CMakeLists.txt b/Reflection/CMakeLists.txt index d124a33..e115a64 100644 --- a/Reflection/CMakeLists.txt +++ b/Reflection/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory( tuplizeAggregate.test ) +add_subdirectory( tagged_ctor_size.test ) diff --git a/Reflection/detail/config.h b/Reflection/detail/config.h index 3fb0a99..cf82900 100644 --- a/Reflection/detail/config.h +++ b/Reflection/detail/config.h @@ -6,3 +6,12 @@ static_assert( __cplusplus > 2020'99 ); // This file will eventually contain all of the constants that control how much generated code the // C++17 Reflection system will geeerate. + +// Note: not exported! +namespace Alepha::Hydrogen::Reflection ::detail:: config_m +{ + namespace C + { + const std::size_t max_ctor_size= 32; + } +} diff --git a/Reflection/tagged_ctor_size.h b/Reflection/tagged_ctor_size.h new file mode 100644 index 0000000..4a9e7ce --- /dev/null +++ b/Reflection/tagged_ctor_size.h @@ -0,0 +1,95 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include +#include +#include + +#include + +#include + +namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_size_m +{ + inline namespace exports {} + + namespace C + { + using namespace Reflection::detail::config_m::C; + } + + // Basic methodology here: I probe the number of arguments that an object can be constructed with, given the tag. + // I don't care what they actually are, as there's no easy way to hoist out what they are. However, for an + // object ctor, we have to get the exact right number. + // Therefore we just wind up finding the smallest-ordinality ctor with that tag. + + // The basic adaptable argument. Because it pretends to be anything, it can be used as a parameter in invoking + // any initialization method. + template< typename Forbidden > + struct argument + { + template< typename T > + requires( not Concepts::SameAs< T, Forbidden > ) + constexpr operator T (); + }; + + template< typename T, typename ... Args > + requires Concepts::ConstructibleFrom< T, Args... > + constexpr void construct( const std::tuple< Args... > & ); + + template< typename T, typename Tuple > + concept ConstructibleWithTuple= + requires( Tuple tup ) + { + { construct< T >( tup ) }; + }; + + template< typename Forbidden, typename Seq > + struct expand; + + template< typename Forbidden, std::size_t ... Ints > + struct expand< Forbidden, std::index_sequence< Ints... > > + { + using type= decltype( std::make_tuple( ( Ints, argument< Forbidden >{} )... ) ); + }; + + template< typename Forbidden, std::size_t argcnt > + using expand_t= typename expand< Forbidden, std::make_index_sequence< argcnt > >::type; + + template< typename T, typename tag, std::size_t args > + concept ConstructibleWith= + ConstructibleWithTuple< T, decltype( std::tuple_cat( std::declval< expand_t< T, args > >(), std::declval< std::tuple< tag > >() ) ) >; + + // The first step is to just start it all off with a blank sequence and walk forward from there. + // The default arguments cause it to start with the blank sequence, even if it doesn't match this + // case in the specialization selection. + template< typename T, typename tag, std::size_t cnt= 0 > + struct has_tagged_ctor + : has_tagged_ctor< T, tag, cnt + 1 > {}; + + template< typename T, typename tag > + struct has_tagged_ctor< T, tag, C::max_ctor_size > + : std::false_type {}; + + template< typename T, typename tag, std::size_t depth > + requires ConstructibleWith< T, tag, depth > + struct has_tagged_ctor< T, tag, depth > + : std::true_type {}; + + namespace exports + { + template< typename T, typename tag > + constexpr std::size_t has_tagged_ctor_v= has_tagged_ctor< T, tag >::value; + + template< typename T, typename tag > + struct has_tagged_ctor : std::bool_constant< has_tagged_ctor_v< T, tag > > {}; + } +} + +namespace Alepha::Hydrogen::Reflection::inline exports::inline tagged_ctor_size_m +{ + using namespace detail::tagged_ctor_size_m::exports; +} diff --git a/Reflection/tagged_ctor_size.test/0.cc b/Reflection/tagged_ctor_size.test/0.cc new file mode 100644 index 0000000..67737ab --- /dev/null +++ b/Reflection/tagged_ctor_size.test/0.cc @@ -0,0 +1,23 @@ +static_assert( __cplusplus > 2020'99 ); + +#include + +#include +#include +#include + +namespace +{ + using namespace Alepha::Testing::literals; + + struct tag {}; + struct tag2 {}; + struct instance + { + explicit instance( int a, float b, char c, double d, tag t ); + }; + + using namespace Alepha::Reflection::exports::tagged_ctor_size_m; + static_assert( has_tagged_ctor_v< instance, tag > ); + static_assert( not has_tagged_ctor_v< instance, tag2 > ); +} diff --git a/Reflection/tagged_ctor_size.test/CMakeLists.txt b/Reflection/tagged_ctor_size.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Reflection/tagged_ctor_size.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From d0fc98de71fa2505d8b33812fba17e9c7ae6b8ee Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 18 Mar 2024 16:13:51 -0400 Subject: [PATCH 02/10] The `has_tagged_ctor` header should have been properly named. --- Reflection/CMakeLists.txt | 2 +- Reflection/{tagged_ctor_size.h => has_tagged_ctor.h} | 8 ++++---- .../{tagged_ctor_size.test => has_tagged_ctor.test}/0.cc | 4 ++-- .../CMakeLists.txt | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename Reflection/{tagged_ctor_size.h => has_tagged_ctor.h} (90%) rename Reflection/{tagged_ctor_size.test => has_tagged_ctor.test}/0.cc (79%) rename Reflection/{tagged_ctor_size.test => has_tagged_ctor.test}/CMakeLists.txt (100%) diff --git a/Reflection/CMakeLists.txt b/Reflection/CMakeLists.txt index e115a64..76ddd72 100644 --- a/Reflection/CMakeLists.txt +++ b/Reflection/CMakeLists.txt @@ -1,2 +1,2 @@ add_subdirectory( tuplizeAggregate.test ) -add_subdirectory( tagged_ctor_size.test ) +add_subdirectory( has_tagged_ctor.test ) diff --git a/Reflection/tagged_ctor_size.h b/Reflection/has_tagged_ctor.h similarity index 90% rename from Reflection/tagged_ctor_size.h rename to Reflection/has_tagged_ctor.h index 4a9e7ce..5fcce43 100644 --- a/Reflection/tagged_ctor_size.h +++ b/Reflection/has_tagged_ctor.h @@ -12,7 +12,7 @@ static_assert( __cplusplus > 2020'99 ); #include -namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_size_m +namespace Alepha::Hydrogen::Reflection ::detail:: has_tagged_ctor_m { inline namespace exports {} @@ -82,14 +82,14 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_size_m namespace exports { template< typename T, typename tag > - constexpr std::size_t has_tagged_ctor_v= has_tagged_ctor< T, tag >::value; + constexpr bool has_tagged_ctor_v= has_tagged_ctor< T, tag >::value; template< typename T, typename tag > struct has_tagged_ctor : std::bool_constant< has_tagged_ctor_v< T, tag > > {}; } } -namespace Alepha::Hydrogen::Reflection::inline exports::inline tagged_ctor_size_m +namespace Alepha::Hydrogen::Reflection::inline exports::inline has_tagged_ctor_m { - using namespace detail::tagged_ctor_size_m::exports; + using namespace detail::has_tagged_ctor_m::exports; } diff --git a/Reflection/tagged_ctor_size.test/0.cc b/Reflection/has_tagged_ctor.test/0.cc similarity index 79% rename from Reflection/tagged_ctor_size.test/0.cc rename to Reflection/has_tagged_ctor.test/0.cc index 67737ab..ffea6cd 100644 --- a/Reflection/tagged_ctor_size.test/0.cc +++ b/Reflection/has_tagged_ctor.test/0.cc @@ -1,6 +1,6 @@ static_assert( __cplusplus > 2020'99 ); -#include +#include #include #include @@ -17,7 +17,7 @@ namespace explicit instance( int a, float b, char c, double d, tag t ); }; - using namespace Alepha::Reflection::exports::tagged_ctor_size_m; + using namespace Alepha::Reflection::exports::has_tagged_ctor_m; static_assert( has_tagged_ctor_v< instance, tag > ); static_assert( not has_tagged_ctor_v< instance, tag2 > ); } diff --git a/Reflection/tagged_ctor_size.test/CMakeLists.txt b/Reflection/has_tagged_ctor.test/CMakeLists.txt similarity index 100% rename from Reflection/tagged_ctor_size.test/CMakeLists.txt rename to Reflection/has_tagged_ctor.test/CMakeLists.txt From 626a3418215ffef5fb61b9f1758befc460d4d12f Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 18 Mar 2024 16:14:15 -0400 Subject: [PATCH 03/10] Tagged ctor count. --- Reflection/CMakeLists.txt | 1 + Reflection/tagged_ctor_count.h | 92 +++++++++++++++++++ Reflection/tagged_ctor_count.test/0.cc | 22 +++++ .../tagged_ctor_count.test/CMakeLists.txt | 1 + 4 files changed, 116 insertions(+) create mode 100644 Reflection/tagged_ctor_count.h create mode 100644 Reflection/tagged_ctor_count.test/0.cc create mode 100644 Reflection/tagged_ctor_count.test/CMakeLists.txt diff --git a/Reflection/CMakeLists.txt b/Reflection/CMakeLists.txt index 76ddd72..2b2b843 100644 --- a/Reflection/CMakeLists.txt +++ b/Reflection/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory( tuplizeAggregate.test ) add_subdirectory( has_tagged_ctor.test ) +add_subdirectory( tagged_ctor_count.test ) diff --git a/Reflection/tagged_ctor_count.h b/Reflection/tagged_ctor_count.h new file mode 100644 index 0000000..66288c1 --- /dev/null +++ b/Reflection/tagged_ctor_count.h @@ -0,0 +1,92 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include +#include +#include + +#include + +#include + +namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m +{ + inline namespace exports {} + + namespace C + { + using namespace Reflection::detail::config_m::C; + } + + // Basic methodology here: I probe the number of arguments that an object can be constructed with, given the tag. + // I don't care what they actually are, as there's no easy way to hoist out what they are. However, for an + // object ctor, we have to get the exact right number. + // Therefore we just wind up finding the smallest-ordinality ctor with that tag. + + // The basic adaptable argument. Because it pretends to be anything, it can be used as a parameter in invoking + // any initialization method. + template< typename Forbidden > + struct argument + { + template< typename T > + requires( not Concepts::SameAs< T, Forbidden > ) + constexpr operator T (); + }; + + template< typename T, typename ... Args > + requires Concepts::ConstructibleFrom< T, Args... > + constexpr void construct( const std::tuple< Args... > & ); + + template< typename T, typename Tuple > + concept ConstructibleWithTuple= + requires( Tuple tup ) + { + { construct< T >( tup ) }; + }; + + template< typename Forbidden, typename Seq > + struct expand; + + template< typename Forbidden, std::size_t ... Ints > + struct expand< Forbidden, std::index_sequence< Ints... > > + { + using type= decltype( std::make_tuple( ( Ints, argument< Forbidden >{} )... ) ); + }; + + template< typename Forbidden, std::size_t argcnt > + using expand_t= typename expand< Forbidden, std::make_index_sequence< argcnt > >::type; + + template< typename T, typename tag, std::size_t args > + concept ConstructibleWith= + ConstructibleWithTuple< T, decltype( std::tuple_cat( std::declval< expand_t< T, args > >(), std::declval< std::tuple< tag > >() ) ) >; + + // The first step is to just start it all off with a blank sequence and walk forward from there. + // The default arguments cause it to start with the blank sequence, even if it doesn't match this + // case in the specialization selection. + template< typename T, typename tag, std::size_t cnt= 0 > + struct tagged_ctor_count + : tagged_ctor_count< T, tag, cnt + 1 > {}; + + template< typename T, typename tag, std::size_t depth > + requires ConstructibleWith< T, tag, depth > + struct tagged_ctor_count< T, tag, depth > + // Size is 1 more than the depth we probed, since that also accounts for the tag. + : std::integral_constant< std::size_t, depth + 1 > {}; + + namespace exports + { + template< typename T, typename tag > + constexpr std::size_t tagged_ctor_count_v= tagged_ctor_count< T, tag >::value; + + template< typename T, typename tag > + struct tagged_ctor_count : std::integral_constant< std::size_t, tagged_ctor_count_v< T, tag > > {}; + } +} + +namespace Alepha::Hydrogen::Reflection::inline exports::inline tagged_ctor_count_m +{ + using namespace detail::tagged_ctor_count_m::exports; +} diff --git a/Reflection/tagged_ctor_count.test/0.cc b/Reflection/tagged_ctor_count.test/0.cc new file mode 100644 index 0000000..8ed5d54 --- /dev/null +++ b/Reflection/tagged_ctor_count.test/0.cc @@ -0,0 +1,22 @@ +static_assert( __cplusplus > 2020'99 ); + +#include + +#include +#include +#include + +namespace +{ + using namespace Alepha::Testing::literals; + + struct tag {}; + struct tag2 {}; + struct instance + { + explicit instance( int a, float b, char c, double d, tag t ); + }; + + using namespace Alepha::Reflection::exports::tagged_ctor_count_m; + static_assert( tagged_ctor_count_v< instance, tag > == 5 ); +} diff --git a/Reflection/tagged_ctor_count.test/CMakeLists.txt b/Reflection/tagged_ctor_count.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Reflection/tagged_ctor_count.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From 35e6089a80ce44b8b16ce67954f496a38ffef144 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 19 Mar 2024 15:02:17 -0400 Subject: [PATCH 04/10] Terminating condition for ctor count. --- Reflection/tagged_ctor_count.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/Reflection/tagged_ctor_count.h b/Reflection/tagged_ctor_count.h index 66288c1..b9210f5 100644 --- a/Reflection/tagged_ctor_count.h +++ b/Reflection/tagged_ctor_count.h @@ -37,8 +37,9 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m }; template< typename T, typename ... Args > - requires Concepts::ConstructibleFrom< T, Args... > - constexpr void construct( const std::tuple< Args... > & ); + requires( Concepts::ConstructibleFrom< T, Args... > ) + constexpr void construct( std::tuple< Args... > && ); + template< typename T, typename Tuple > concept ConstructibleWithTuple= @@ -66,13 +67,31 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m // The first step is to just start it all off with a blank sequence and walk forward from there. // The default arguments cause it to start with the blank sequence, even if it doesn't match this // case in the specialization selection. - template< typename T, typename tag, std::size_t cnt= 0 > + template< typename T, typename tag, std::size_t cnt= 0, typename= void > struct tagged_ctor_count : tagged_ctor_count< T, tag, cnt + 1 > {}; + template< typename T, typename tag > + struct tagged_ctor_count< T, tag, C::max_ctor_size + 1 > + { + struct impossible; + static_assert( std::is_same_v< impossible, T >, "Max depth reached." ); + }; + template< typename T, typename tag, std::size_t depth > - requires ConstructibleWith< T, tag, depth > - struct tagged_ctor_count< T, tag, depth > + struct tagged_ctor_count + < + T, + tag, + depth, + std::void_t + < + decltype( construct< T > + ( + std::tuple_cat( std::declval< expand_t< T, depth > >(), std::declval< std::tuple< tag > >() ) + ) ) + > + > // Size is 1 more than the depth we probed, since that also accounts for the tag. : std::integral_constant< std::size_t, depth + 1 > {}; From 3707ec5cc2a8bcfe2c3275923f6c43b541657625 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 19 Mar 2024 15:21:59 -0400 Subject: [PATCH 05/10] We can use requires clauses here. But it does seem that I can't remove the repeated defns for the argument extraction. --- Reflection/tagged_ctor_count.h | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/Reflection/tagged_ctor_count.h b/Reflection/tagged_ctor_count.h index b9210f5..13f8663 100644 --- a/Reflection/tagged_ctor_count.h +++ b/Reflection/tagged_ctor_count.h @@ -36,6 +36,10 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m constexpr operator T (); }; + template< typename T, typename ... Args > + requires( Concepts::ConstructibleFrom< T, Args... > ) + constexpr void construct( const std::tuple< Args... > & ); + template< typename T, typename ... Args > requires( Concepts::ConstructibleFrom< T, Args... > ) constexpr void construct( std::tuple< Args... > && ); @@ -67,7 +71,7 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m // The first step is to just start it all off with a blank sequence and walk forward from there. // The default arguments cause it to start with the blank sequence, even if it doesn't match this // case in the specialization selection. - template< typename T, typename tag, std::size_t cnt= 0, typename= void > + template< typename T, typename tag, std::size_t cnt= 0 > struct tagged_ctor_count : tagged_ctor_count< T, tag, cnt + 1 > {}; @@ -79,19 +83,8 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_count_m }; template< typename T, typename tag, std::size_t depth > - struct tagged_ctor_count - < - T, - tag, - depth, - std::void_t - < - decltype( construct< T > - ( - std::tuple_cat( std::declval< expand_t< T, depth > >(), std::declval< std::tuple< tag > >() ) - ) ) - > - > + requires( ConstructibleWith< T, tag, depth > ) + struct tagged_ctor_count< T, tag, depth > // Size is 1 more than the depth we probed, since that also accounts for the tag. : std::integral_constant< std::size_t, depth + 1 > {}; From 1e3b3707e281d9ccd5117d29a4a9c18faa70e8f0 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 21 Mar 2024 15:31:02 -0400 Subject: [PATCH 06/10] Fixed tagged ctor argument inspection code. --- Reflection/CMakeLists.txt | 1 + Reflection/tagged_ctor_arg.h | 234 ++++++++++++++++++ Reflection/tagged_ctor_arg.test/0.cc | 47 ++++ .../tagged_ctor_arg.test/CMakeLists.txt | 1 + 4 files changed, 283 insertions(+) create mode 100644 Reflection/tagged_ctor_arg.h create mode 100644 Reflection/tagged_ctor_arg.test/0.cc create mode 100644 Reflection/tagged_ctor_arg.test/CMakeLists.txt diff --git a/Reflection/CMakeLists.txt b/Reflection/CMakeLists.txt index 2b2b843..493c869 100644 --- a/Reflection/CMakeLists.txt +++ b/Reflection/CMakeLists.txt @@ -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 ) diff --git a/Reflection/tagged_ctor_arg.h b/Reflection/tagged_ctor_arg.h new file mode 100644 index 0000000..c4b3388 --- /dev/null +++ b/Reflection/tagged_ctor_arg.h @@ -0,0 +1,234 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +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; +} + diff --git a/Reflection/tagged_ctor_arg.test/0.cc b/Reflection/tagged_ctor_arg.test/0.cc new file mode 100644 index 0000000..fc61b3e --- /dev/null +++ b/Reflection/tagged_ctor_arg.test/0.cc @@ -0,0 +1,47 @@ +static_assert( __cplusplus > 2020'99 ); + +#include + +#include + +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 > > ); +} diff --git a/Reflection/tagged_ctor_arg.test/CMakeLists.txt b/Reflection/tagged_ctor_arg.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Reflection/tagged_ctor_arg.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From 892a557ce7962aaec801f8ee3b614a0b8b433730 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 21 Mar 2024 15:36:54 -0400 Subject: [PATCH 07/10] Modernize a bit with some requires statements. --- Reflection/tagged_ctor_arg.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Reflection/tagged_ctor_arg.h b/Reflection/tagged_ctor_arg.h index c4b3388..e868fe2 100644 --- a/Reflection/tagged_ctor_arg.h +++ b/Reflection/tagged_ctor_arg.h @@ -64,11 +64,17 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_arg_m template< typename Forbidden > struct argument { - template< typename T, typename= std::enable_if_t< not std::is_same_v< T, Forbidden > > > + template< typename T > + requires( not Concepts::SameAs< T, Forbidden > ) constexpr operator T (); }; - template< typename T, typename ... Args, typename= std::enable_if_t< std::is_constructible_v< T, Args... > > > + template< typename T, typename ... Args > + requires( Concepts::ConstructibleFrom< T, Args... > ) + constexpr void construct( const std::tuple< Args... > & ); + + template< typename T, typename ... Args > + requires( Concepts::ConstructibleFrom< T, Args... > ) constexpr void construct( std::tuple< Args... > && ); template From 3259543c30161dcf441091a96839a683eb091d1d Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 21 Mar 2024 16:13:30 -0400 Subject: [PATCH 08/10] Tuple concatenation. --- Meta/tuple_cat.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Meta/tuple_cat.h diff --git a/Meta/tuple_cat.h b/Meta/tuple_cat.h new file mode 100644 index 0000000..79f7079 --- /dev/null +++ b/Meta/tuple_cat.h @@ -0,0 +1,24 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include + +#include + +namespace Alepha::Hydrogen::Meta ::detail:: tuple_cat_m +{ + inline namespace exports + { + template< typename Left, typename Right > + using tuple_cat_t= tuple_from_list_t< list_cat_t< list_from_tuple_t< Left >, list_from_tuple_t< Right > > >; + } +} + +namespace Alepha::Hydrogen::Meta::inline exports::inline tuple_cat_m +{ + using namespace detail::tuple_cat_m::exports; +} + From c6f86ed4bacfbea4ca0c39e7ad6a3dd0b3aed09e Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 21 Mar 2024 16:20:26 -0400 Subject: [PATCH 09/10] Add a repeat function to type lisp. --- type_lisp.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/type_lisp.h b/type_lisp.h index ca2331f..b8e36bd 100644 --- a/type_lisp.h +++ b/type_lisp.h @@ -141,6 +141,27 @@ namespace Alepha::Hydrogen ::detail:: type_lisp_m { using type= TypeList< LeftArgs..., RightArgs... >; }; + + template< typename Item, std::size_t count > + struct repeat; + + namespace exports + { + template< typename Item, std::size_t count > + using repeat_t= typename repeat< Item, count >::type; + } + + template< typename Item, std::size_t count > + struct repeat + { + using type= cons_t< Item, repeat_t< Item, count - 1 > >; + }; + + template< typename Item > + struct repeat< Item, 0 > + { + using type= Nil; + }; } namespace Alepha::Hydrogen::inline exports::inline type_lisp_m From d9b2362b54d9abb109ebfa078cd9ee43174602bf Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 21 Mar 2024 16:21:08 -0400 Subject: [PATCH 10/10] Start introducing more requires clauses. --- Reflection/tagged_ctor_arg.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Reflection/tagged_ctor_arg.h b/Reflection/tagged_ctor_arg.h index e868fe2..d1f2283 100644 --- a/Reflection/tagged_ctor_arg.h +++ b/Reflection/tagged_ctor_arg.h @@ -9,6 +9,9 @@ static_assert( __cplusplus > 2020'99 ); #include #include +#include + +#include #include @@ -77,6 +80,13 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_arg_m requires( Concepts::ConstructibleFrom< T, Args... > ) constexpr void construct( std::tuple< Args... > && ); + template< typename T, typename Tuple > + concept ConstructibleWithTuple= + requires( Tuple tup ) + { + { construct< T >( tup ) }; + }; + template < typename T, @@ -105,6 +115,15 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_arg_m typename tag, std::size_t ... positions > + requires + ( + ConstructibleWithTuple + < + T, + Meta::tuple_cat_t< + tuple_from_list_t< repeat_t< argument< T >, sizeof...( positions ) > >, std::tuple< tag > > + > + ) struct extract_all_ctor_arguments < T, @@ -123,6 +142,16 @@ namespace Alepha::Hydrogen::Reflection ::detail:: tagged_ctor_arg_m > > { + static_assert + ( + ConstructibleWithTuple + < + T, + Meta::tuple_cat_t< + tuple_from_list_t< repeat_t< argument< T >, sizeof...( positions ) > >, std::tuple< tag > > + > + ); + template < typename int_const,