forked from Alepha/Alepha
Merge in some reflection improvements.
This is some of the reflection improvements I've implemented. I do need to solve a few issues with arity (perhaps?) I also would like to introduce more requires clauses and retire some C++17 "concepts" hacks. However, it seems that the way that requires clauses expand things is in such a way that it prevents friend injection?
This commit is contained in:
@ -1 +1,4 @@
|
||||
add_subdirectory( tuplizeAggregate.test )
|
||||
add_subdirectory( has_tagged_ctor.test )
|
||||
add_subdirectory( tagged_ctor_count.test )
|
||||
add_subdirectory( tagged_ctor_arg.test )
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
95
Reflection/has_tagged_ctor.h
Normal file
95
Reflection/has_tagged_ctor.h
Normal file
@ -0,0 +1,95 @@
|
||||
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>
|
||||
|
||||
namespace Alepha::Hydrogen::Reflection ::detail:: has_tagged_ctor_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 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 has_tagged_ctor_m
|
||||
{
|
||||
using namespace detail::has_tagged_ctor_m::exports;
|
||||
}
|
||||
23
Reflection/has_tagged_ctor.test/0.cc
Normal file
23
Reflection/has_tagged_ctor.test/0.cc
Normal file
@ -0,0 +1,23 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include <Alepha/Reflection/has_tagged_ctor.h>
|
||||
|
||||
#include <Alepha/Testing/test.h>
|
||||
#include <Alepha/types.h>
|
||||
#include <Alepha/Meta/product_type_decay.h>
|
||||
|
||||
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::has_tagged_ctor_m;
|
||||
static_assert( has_tagged_ctor_v< instance, tag > );
|
||||
static_assert( not has_tagged_ctor_v< instance, tag2 > );
|
||||
}
|
||||
1
Reflection/has_tagged_ctor.test/CMakeLists.txt
Normal file
1
Reflection/has_tagged_ctor.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
unit_test( 0 )
|
||||
269
Reflection/tagged_ctor_arg.h
Normal file
269
Reflection/tagged_ctor_arg.h
Normal file
@ -0,0 +1,269 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include <Alepha/Concepts.h>
|
||||
#include <Alepha/type_lisp.h>
|
||||
|
||||
#include <Alepha/Meta/tuple_cat.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 >
|
||||
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 ... Args >
|
||||
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,
|
||||
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
|
||||
>
|
||||
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,
|
||||
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 >() )
|
||||
) )
|
||||
)
|
||||
>
|
||||
>
|
||||
{
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
47
Reflection/tagged_ctor_arg.test/0.cc
Normal file
47
Reflection/tagged_ctor_arg.test/0.cc
Normal 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 > > );
|
||||
}
|
||||
1
Reflection/tagged_ctor_arg.test/CMakeLists.txt
Normal file
1
Reflection/tagged_ctor_arg.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
unit_test( 0 )
|
||||
104
Reflection/tagged_ctor_count.h
Normal file
104
Reflection/tagged_ctor_count.h
Normal file
@ -0,0 +1,104 @@
|
||||
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>
|
||||
|
||||
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 ... Args >
|
||||
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 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 >
|
||||
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 >
|
||||
// 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;
|
||||
}
|
||||
22
Reflection/tagged_ctor_count.test/0.cc
Normal file
22
Reflection/tagged_ctor_count.test/0.cc
Normal file
@ -0,0 +1,22 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include <Alepha/Reflection/tagged_ctor_count.h>
|
||||
|
||||
#include <Alepha/Testing/test.h>
|
||||
#include <Alepha/types.h>
|
||||
#include <Alepha/Meta/product_type_decay.h>
|
||||
|
||||
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 );
|
||||
}
|
||||
1
Reflection/tagged_ctor_count.test/CMakeLists.txt
Normal file
1
Reflection/tagged_ctor_count.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
unit_test( 0 )
|
||||
Reference in New Issue
Block a user