forked from Alepha/Alepha
Apply the newer namespace rules and layout/formatting.
This commit is contained in:
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Algorithm::detail::string_distance_m
|
namespace Alepha::Hydrogen::Algorithm ::detail:: string_distance_m
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
115
AutoRAII.h
115
AutoRAII.h
@ -9,78 +9,73 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen
|
namespace Alepha::Hydrogen ::detail:: AutoRAII_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace AutoRAII_m {} }
|
inline namespace exports
|
||||||
|
|
||||||
namespace detail::AutoRAII_m
|
|
||||||
{
|
{
|
||||||
inline namespace exports
|
template< typename T, typename Dtor= std::function< void ( T ) > >
|
||||||
|
class AutoRAII : boost::noncopyable
|
||||||
{
|
{
|
||||||
template< typename T, typename Dtor= std::function< void ( T ) > >
|
private:
|
||||||
class AutoRAII : boost::noncopyable
|
Dtor dtor;
|
||||||
{
|
T value;
|
||||||
private:
|
|
||||||
Dtor dtor;
|
|
||||||
T value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~AutoRAII()
|
~AutoRAII()
|
||||||
|
{
|
||||||
|
if constexpr( std::is_same_v< Dtor, std::function< void ( T ) > > )
|
||||||
{
|
{
|
||||||
if constexpr( std::is_same_v< Dtor, std::function< void ( T ) > > )
|
if( dtor == nullptr ) return;
|
||||||
{
|
|
||||||
if( dtor == nullptr ) return;
|
|
||||||
}
|
|
||||||
dtor( value );
|
|
||||||
}
|
}
|
||||||
|
dtor( value );
|
||||||
|
}
|
||||||
|
|
||||||
template< typename Ctor >
|
template< typename Ctor >
|
||||||
explicit AutoRAII( Ctor ctor, Dtor dtor ) : dtor( std::move( dtor ) ), value( ctor() ) {}
|
explicit AutoRAII( Ctor ctor, Dtor dtor ) : dtor( std::move( dtor ) ), value( ctor() ) {}
|
||||||
|
|
||||||
operator const T &() const { return value; }
|
operator const T &() const { return value; }
|
||||||
|
|
||||||
decltype( auto )
|
decltype( auto )
|
||||||
operator *() const
|
operator *() const
|
||||||
requires( std::is_pointer_v< T > )
|
requires( std::is_pointer_v< T > )
|
||||||
|
{
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype( auto )
|
||||||
|
operator->() const
|
||||||
|
requires( std::is_pointer_v< T > )
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename Dtor >
|
||||||
|
class AutoRAII< void, Dtor > : boost::noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Dtor dtor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~AutoRAII()
|
||||||
|
{
|
||||||
|
if constexpr( std::is_same_v< Dtor, std::function< void () > > )
|
||||||
{
|
{
|
||||||
return *value;
|
if( dtor == nullptr ) return;
|
||||||
}
|
}
|
||||||
|
dtor();
|
||||||
decltype( auto )
|
}
|
||||||
operator->() const
|
|
||||||
requires( std::is_pointer_v< T > )
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template< typename Dtor >
|
template< typename Ctor >
|
||||||
class AutoRAII< void, Dtor > : boost::noncopyable
|
explicit AutoRAII( Ctor ctor, Dtor dtor ) : dtor( std::move( dtor ) ) { ctor(); }
|
||||||
{
|
};
|
||||||
private:
|
|
||||||
Dtor dtor;
|
|
||||||
|
|
||||||
public:
|
template< typename Ctor, typename Dtor >
|
||||||
~AutoRAII()
|
explicit AutoRAII( Ctor ctor, Dtor ) -> AutoRAII< decltype( ctor() ), Dtor >;
|
||||||
{
|
|
||||||
if constexpr( std::is_same_v< Dtor, std::function< void () > > )
|
|
||||||
{
|
|
||||||
if( dtor == nullptr ) return;
|
|
||||||
}
|
|
||||||
dtor();
|
|
||||||
}
|
|
||||||
|
|
||||||
template< typename Ctor >
|
|
||||||
explicit AutoRAII( Ctor ctor, Dtor dtor ) : dtor( std::move( dtor ) ) { ctor(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template< typename Ctor, typename Dtor >
|
|
||||||
explicit AutoRAII( Ctor ctor, Dtor ) -> AutoRAII< decltype( ctor() ), Dtor >;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace exports::AutoRAII_m
|
|
||||||
{
|
|
||||||
using namespace detail::AutoRAII_m::exports;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::inline exports::inline AutoRAII_m
|
||||||
|
{
|
||||||
|
using namespace detail::AutoRAII_m::exports;
|
||||||
|
}
|
||||||
|
@ -46,7 +46,7 @@ namespace Alepha::Hydrogen ::detail:: Console_m
|
|||||||
{
|
{
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers_m;
|
||||||
using Utility::StaticValue;
|
using Utility::StaticValue;
|
||||||
|
|
||||||
namespace C
|
namespace C
|
||||||
|
@ -12,7 +12,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "Blob.h"
|
#include "Blob.h"
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: data_chain
|
namespace Alepha::inline Cavorite ::detail:: DataChain_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include "delimiters.h"
|
#include "delimiters.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams ::detail:: istreamable_module
|
namespace Alepha::Hydrogen::IOStreams ::detail:: IStreamable_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -57,7 +57,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: istreamable_module
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams::inline exports::inline istreamable_module
|
namespace Alepha::Hydrogen::IOStreams::inline exports::inline IStreamable_m
|
||||||
{
|
{
|
||||||
using namespace detail::istreamable_module::exports;
|
using namespace detail::IStreamable_m::exports;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/IOStreams/delimiters.h>
|
#include <Alepha/IOStreams/delimiters.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams ::detail:: ostreamable_module
|
namespace Alepha::Hydrogen::IOStreams ::detail:: OStreamable_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -54,8 +54,8 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: ostreamable_module
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams::inline exports::inline ostreamable_module
|
namespace Alepha::Hydrogen::IOStreams::inline exports::inline OStreamable_m
|
||||||
{
|
{
|
||||||
using namespace detail::ostreamable_module::exports;
|
using namespace detail::OStreamable_m::exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams::detail::stackable_streambuf
|
namespace Alepha::Hydrogen::IOStreams::detail::StackableStreambuf_m
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams ::detail:: stackable_streambuf
|
namespace Alepha::Hydrogen::IOStreams ::detail:: StackableStreambuf_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -63,7 +63,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stackable_streambuf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams::inline exports::inline stackable_streambuf
|
namespace Alepha::Hydrogen::IOStreams::inline exports::inline StackableStreambuf_m
|
||||||
{
|
{
|
||||||
using namespace detail::stackable_streambuf::exports;
|
using namespace detail::StackableStreambuf_m::exports;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state
|
namespace Alepha::Hydrogen::IOStreams ::detail:: StreamState_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -124,7 +124,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::IOStreams::inline exports::inline stream_state
|
namespace Alepha::Hydrogen::IOStreams::inline exports::inline StreamState_m
|
||||||
{
|
{
|
||||||
using namespace detail::stream_state::exports;
|
using namespace detail::StreamState_m::exports;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using namespace Alepha::Utility::exports::evaluation_helpers;
|
using namespace Alepha::Utility::exports::evaluation_helpers_m;
|
||||||
using namespace Alepha::Testing::literals;
|
using namespace Alepha::Testing::literals;
|
||||||
|
|
||||||
using std::begin, std::end;
|
using std::begin, std::end;
|
||||||
|
@ -4,21 +4,16 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Meta
|
namespace Alepha::Hydrogen::Meta ::detail:: dep_value_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace dependent_value {} }
|
inline namespace exports
|
||||||
|
|
||||||
namespace detail::dependent_value
|
|
||||||
{
|
{
|
||||||
inline namespace exports
|
template< auto value, typename T >
|
||||||
{
|
constexpr auto dep_value= value;
|
||||||
template< auto value, typename T >
|
|
||||||
constexpr auto dep_value= value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace exports::dependent_value
|
|
||||||
{
|
|
||||||
using namespace detail::dependent_value::exports;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Meta::inline exports::inline dep_value_m
|
||||||
|
{
|
||||||
|
using namespace detail::dep_value_m::exports;
|
||||||
|
}
|
||||||
|
43
Meta/find.h
43
Meta/find.h
@ -9,36 +9,31 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <Alepha/Meta/Container/vector.h>
|
#include <Alepha/Meta/Container/vector.h>
|
||||||
#include <Alepha/Meta/functional.h>
|
#include <Alepha/Meta/functional.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Meta
|
namespace Alepha::Hydrogen::Meta ::detail:: find_algo_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace find_algos {} }
|
inline namespace exports
|
||||||
|
|
||||||
namespace detail::find_algos
|
|
||||||
{
|
{
|
||||||
inline namespace exports
|
template< typename Iter, typename Predicate >
|
||||||
|
constexpr bool
|
||||||
|
find_if( const Iter first, const Iter last, Predicate pred )
|
||||||
{
|
{
|
||||||
template< typename Iter, typename Predicate >
|
for( Iter pos= first; pos != last; ++pos )
|
||||||
constexpr bool
|
|
||||||
find_if( const Iter first, const Iter last, Predicate pred )
|
|
||||||
{
|
{
|
||||||
for( Iter pos= first; pos != last; ++pos )
|
if( pred( *pos ) ) return true;
|
||||||
{
|
|
||||||
if( pred( *pos ) ) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
template< typename Iter, typename Value >
|
template< typename Iter, typename Value >
|
||||||
constexpr bool
|
constexpr bool
|
||||||
find( const Iter first, const Iter last, const Value value )
|
find( const Iter first, const Iter last, const Value value )
|
||||||
{
|
{
|
||||||
return find_if( first, last, Meta::bind1st( std::equal_to{}, value ) );
|
return find_if( first, last, Meta::bind1st( std::equal_to{}, value ) );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
namespace exports::find_algos
|
|
||||||
{
|
namespace Alepha::Hydrogen::Meta::inline exports::inline find_algo_m
|
||||||
using namespace detail::find_algos::exports;
|
{
|
||||||
}
|
using namespace detail::find_algo_m::exports;
|
||||||
}
|
}
|
||||||
|
@ -6,51 +6,46 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Meta
|
namespace Alepha::Hydrogen::Meta ::detail:: functional_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace functional {} }
|
inline namespace exports
|
||||||
|
|
||||||
namespace detail::functional
|
|
||||||
{
|
{
|
||||||
inline namespace exports
|
|
||||||
{
|
|
||||||
template< typename MetaFunction, typename Arg >
|
|
||||||
constexpr auto bind1st( MetaFunction func, Arg arg );
|
|
||||||
}
|
|
||||||
|
|
||||||
template< typename MetaFunction, typename Arg > struct binder1st;
|
|
||||||
|
|
||||||
template< typename MetaFunction, typename Arg1, typename Arg2 >
|
|
||||||
constexpr decltype( auto )
|
|
||||||
invoke_call( MetaFunction func, Meta::type_value< Arg1 > arg1, Meta::type_value< Arg2 > arg2 )
|
|
||||||
{
|
|
||||||
return func( arg1, arg2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
template< typename MetaFunction, typename Arg >
|
template< typename MetaFunction, typename Arg >
|
||||||
struct binder1st< MetaFunction, Meta::type_value< Arg > >
|
constexpr auto bind1st( MetaFunction func, Arg arg );
|
||||||
{
|
|
||||||
MetaFunction func;
|
|
||||||
Meta::type_value< Arg > arg;
|
|
||||||
|
|
||||||
template< typename Second >
|
|
||||||
constexpr decltype( auto )
|
|
||||||
operator () ( const Second &second )
|
|
||||||
{
|
|
||||||
return invoke_call( func, arg, second );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template< typename MetaFunction, typename Arg >
|
|
||||||
constexpr auto
|
|
||||||
exports::bind1st( MetaFunction func, Arg arg )
|
|
||||||
{
|
|
||||||
return binder1st< MetaFunction, Arg >{ func, arg };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace exports::functional
|
template< typename MetaFunction, typename Arg > struct binder1st;
|
||||||
|
|
||||||
|
template< typename MetaFunction, typename Arg1, typename Arg2 >
|
||||||
|
constexpr decltype( auto )
|
||||||
|
invoke_call( MetaFunction func, Meta::type_value< Arg1 > arg1, Meta::type_value< Arg2 > arg2 )
|
||||||
{
|
{
|
||||||
using namespace detail::functional::exports;
|
return func( arg1, arg2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename MetaFunction, typename Arg >
|
||||||
|
struct binder1st< MetaFunction, Meta::type_value< Arg > >
|
||||||
|
{
|
||||||
|
MetaFunction func;
|
||||||
|
Meta::type_value< Arg > arg;
|
||||||
|
|
||||||
|
template< typename Second >
|
||||||
|
constexpr decltype( auto )
|
||||||
|
operator () ( const Second &second )
|
||||||
|
{
|
||||||
|
return invoke_call( func, arg, second );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename MetaFunction, typename Arg >
|
||||||
|
constexpr auto
|
||||||
|
exports::bind1st( MetaFunction func, Arg arg )
|
||||||
|
{
|
||||||
|
return binder1st< MetaFunction, Arg >{ func, arg };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Meta::inline exports::inline functional_m
|
||||||
|
{
|
||||||
|
using namespace detail::functional_m::exports;
|
||||||
|
}
|
||||||
|
@ -100,7 +100,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
{
|
{
|
||||||
inline namespace exports {}
|
inline namespace exports {}
|
||||||
|
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* User created unique symbols can be bound to options to build classes of options.
|
* User created unique symbols can be bound to options to build classes of options.
|
||||||
|
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Reflection/detail/config.h>
|
#include <Alepha/Reflection/detail/config.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Reflection ::detail:: aggregate_initializer_size_m
|
namespace Alepha::Hydrogen::Reflection ::detail:: aggregate_initializer_size_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
inline namespace exports {}
|
||||||
|
|
||||||
|
@ -16,176 +16,171 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <Alepha/Meta/overload.h>
|
#include <Alepha/Meta/overload.h>
|
||||||
#include <Alepha/Meta/type_value.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 >
|
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 >
|
//template< typename T > constexpr operator T ()= delete;
|
||||||
constexpr bool is_empty_base_v< empty_base< T > >{ true };
|
};
|
||||||
|
|
||||||
template< typename Tuple, std::size_t baseCount, std::size_t totalCount >
|
template< typename T >
|
||||||
constexpr void
|
constexpr bool is_empty_base_v= false;
|
||||||
check_tuple()
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
template< typename Aggregate, std::size_t bases, std::size_t total >
|
|
||||||
constexpr auto
|
|
||||||
build_init_tuple()
|
|
||||||
{
|
{
|
||||||
static_assert( bases <= total );
|
static_assert( bases == 0 );
|
||||||
if constexpr( total == 0 ) return std::tuple{};
|
auto result= std::tuple_cat( std::tuple{ argument{} }, build_init_tuple< Aggregate, 0, total - 1 >() );
|
||||||
else if constexpr( bases > 0 )
|
check_tuple< decltype( result ), bases, total >();
|
||||||
{
|
return result;
|
||||||
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 > > {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
@ -11,197 +11,192 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <Alepha/Reflection/detail/config.h>
|
#include <Alepha/Reflection/detail/config.h>
|
||||||
#include <Alepha/Reflection/aggregate_members.h>
|
#include <Alepha/Reflection/aggregate_members.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Reflection
|
namespace Alepha::Hydrogen::Reflection ::detail:: tuplizeAggregate_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace tuplize_aggregate {} }
|
inline namespace exports {}
|
||||||
|
|
||||||
namespace detail::tuplize_aggregate
|
template< typename T >
|
||||||
|
concept SalientMembers=
|
||||||
|
requires( const T &t )
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
{ T::salient_members } -> ConvertibleTo< std::size_t >;
|
||||||
|
};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept SalientMembers=
|
constexpr std::size_t
|
||||||
requires( const T &t )
|
compute_salient_members_count_impl()
|
||||||
{
|
{
|
||||||
{ T::salient_members } -> ConvertibleTo< std::size_t >;
|
if constexpr( SalientMembers< T > ) return T::salient_members;
|
||||||
};
|
else return aggregate_member_count_v< std::decay_t< T > >;
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
constexpr std::size_t
|
|
||||||
compute_salient_members_count_impl()
|
|
||||||
{
|
|
||||||
if constexpr( SalientMembers< T > ) return T::salient_members;
|
|
||||||
else return aggregate_member_count_v< std::decay_t< T > >;
|
|
||||||
}
|
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
constexpr std::size_t compute_salient_members_count_v= compute_salient_members_count_impl< T >();
|
|
||||||
|
|
||||||
namespace exports
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* Deconstruct an aggregate object into a tie-based tuple pointing at its members.
|
|
||||||
*
|
|
||||||
* C++17's primary new reflection-oriented introduction is Structured Binding Declarations.
|
|
||||||
* What these let one do is to introduce a set of named variables that bind to each member
|
|
||||||
* of a (raw aggregate) `struct` in turn. This leads to some very interesting forms of
|
|
||||||
* "reflection" about what a user defined type is made of. Combined with the `std::is_aggregate`
|
|
||||||
* trait function and a way to determine the number of member objects, this provides a
|
|
||||||
* powerful new way to inspect any type.
|
|
||||||
*
|
|
||||||
* Structured Binding Declarations can also be used with arrays or types which implement a subset
|
|
||||||
* of the `std::tuple` interface. Those cases are not as interesting. We've always had the
|
|
||||||
* ability to inspect arrays via templates -- simple deduction operations work for that. And
|
|
||||||
* C++11's `std::tuple`s are already inspectable by their nature and types which implement a tuple-like
|
|
||||||
* interface are also easily inspected by pre-C++17 means.
|
|
||||||
*
|
|
||||||
* The most important thing C++17 Structured Binding brings to the language is the ability to
|
|
||||||
* (at compiletime) programmatically inspect any structure's members -- to learn their types,
|
|
||||||
* and with a bit of special effort, to learn their offsets. The names of those members are
|
|
||||||
* hidden, but their types are available, as is a way to work with all of them at once. Any
|
|
||||||
* Structured Binding is sufficient to do this -- one need only give a new name for each member of
|
|
||||||
* the type. `auto &[ a, b, c, d ]= someStruct;` is all that is needed and one has already performed
|
|
||||||
* an interesting feat of rudimentary reflection on the type `someStruct`. By loading those values
|
|
||||||
* into a tuple (by reference), by code such as `std::tie( a, b, c, d )`, a programmer can provide
|
|
||||||
* an anonymized, distilled reflection of the contents of that `struct`. This said, a library function
|
|
||||||
* which can decompose any `struct` into such a tie is very useful. `tuplizeAggregate` is exactly this.
|
|
||||||
*
|
|
||||||
* This function contains a pre-built set of such decompositions for structs of various sizes. C++17
|
|
||||||
* does not permit arbitrarily sized Structured Bindings, and so a limit had to be placed. The limit
|
|
||||||
* is fairly generous, however. If an aggregate size which is greater than the pre-build maximum is
|
|
||||||
* provided, then the compile will fail on a `static_assert` indicating this.
|
|
||||||
*
|
|
||||||
* Unfortunately, as a declaration syntax, the number of members in a `struct`'s body cannot be inferred
|
|
||||||
* through SFINAE by this means. Normally the user must explicitly provide the number of member
|
|
||||||
* variables. However, combined with a pair of C++11 features (based upon variadic templates and
|
|
||||||
* aggregate initialization syntax) we can infer the number of memmber values via a set of helper
|
|
||||||
* templates (which can also be called directly.)
|
|
||||||
*
|
|
||||||
* This kind of reflection into an aggregate type can prove very useful. Code generators for
|
|
||||||
* serialization, conversion tools, universal utility functions, and much more can all be built in
|
|
||||||
* C++17, today, using this kind of reflection! There's no need to wait for C++23 or beyond when
|
|
||||||
* static reflection is added to the language. A great deal of desired reflection use cases can be
|
|
||||||
* attained today. One just need write some code generators in terms of `std::tuple` and `std::tie`,
|
|
||||||
* then make any overloads (perhaps using ADL hooking tricks) which call `Alepha::Reflection::tuplizeAggregate`
|
|
||||||
* and pass that result to the general tuple form. For serializers and such, other techniques such as
|
|
||||||
* `boost::core::demangle( typeid( instance ).name() )` can be used to get nice names for types when
|
|
||||||
* implementing universal serializers. In fact, this can be used as a crutch for serializing more
|
|
||||||
* complicated user types (with private data and such). Those types can produce an aggregate "view"
|
|
||||||
* of what they must serialize or deserialize, and then they can hand that view off to such code
|
|
||||||
* generators. And, of cousre, this need not apply just to serialization.
|
|
||||||
*
|
|
||||||
* @param agg Aggregate instance to decompose into a `std::tie` based `std::tuple`.
|
|
||||||
* @tparam aggregate_size The number of members in the aggregate argument `agg`'s definition.
|
|
||||||
* @tparam Aggregate The type of the aggregate to decompose.
|
|
||||||
*/
|
|
||||||
// TODO: Make `aggregate_size` deduced via `Reflection::aggregate_ctor...` means.
|
|
||||||
template< std::size_t aggregate_size, typename Aggregate, typename= std::enable_if_t< not std::is_rvalue_reference_v< Aggregate > > >
|
|
||||||
constexpr decltype( auto )
|
|
||||||
tuplizeAggregate( Aggregate &&agg )
|
|
||||||
{
|
|
||||||
static_assert( std::is_aggregate_v< std::decay_t< Aggregate > >, "`tuplizeAggregate` only can be used on aggregates" );
|
|
||||||
|
|
||||||
// TODO: Generate these cases via boost preprocessor, to cut down on repetition...
|
|
||||||
if constexpr( aggregate_size == 0 ) return std::tuple{};
|
|
||||||
else if constexpr( aggregate_size == 1 )
|
|
||||||
{
|
|
||||||
auto &[ a0 ]= agg;
|
|
||||||
return std::tie( a0 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 2 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1 ]= agg;
|
|
||||||
return std::tie( a0, a1 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 3 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 4 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 5 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 6 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 7 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 8 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 9 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 10 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 11 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 12 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 13 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 14 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 15 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 );
|
|
||||||
}
|
|
||||||
else if constexpr( aggregate_size == 16 )
|
|
||||||
{
|
|
||||||
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 ]= agg;
|
|
||||||
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 );
|
|
||||||
}
|
|
||||||
// Impossible, in this case -- we would have taken the original 0 branch were this so.
|
|
||||||
else static_assert( aggregate_size == 0, "The specified aggregate has more members than `tuplizeAggregate` can handle" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// This overload deduces the aggregate size using the initializer inspection utilities.
|
|
||||||
template< typename Aggregate >
|
|
||||||
constexpr decltype( auto )
|
|
||||||
tuplizeAggregate( Aggregate &&agg )
|
|
||||||
{
|
|
||||||
return tuplizeAggregate< compute_salient_members_count_v< std::decay_t< Aggregate > > >( std::forward< Aggregate >( agg ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
template< typename Aggregate >
|
|
||||||
using aggregate_tuple_t= decay_tuple_t< std::decay_t< decltype( tuplizeAggregate( std::declval< const Aggregate & >() ) ) > >;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace exports::tuplize_aggregate
|
template< typename T >
|
||||||
|
constexpr std::size_t compute_salient_members_count_v= compute_salient_members_count_impl< T >();
|
||||||
|
|
||||||
|
namespace exports
|
||||||
{
|
{
|
||||||
using namespace detail::tuplize_aggregate::exports;
|
/*!
|
||||||
|
* Deconstruct an aggregate object into a tie-based tuple pointing at its members.
|
||||||
|
*
|
||||||
|
* C++17's primary new reflection-oriented introduction is Structured Binding Declarations.
|
||||||
|
* What these let one do is to introduce a set of named variables that bind to each member
|
||||||
|
* of a (raw aggregate) `struct` in turn. This leads to some very interesting forms of
|
||||||
|
* "reflection" about what a user defined type is made of. Combined with the `std::is_aggregate`
|
||||||
|
* trait function and a way to determine the number of member objects, this provides a
|
||||||
|
* powerful new way to inspect any type.
|
||||||
|
*
|
||||||
|
* Structured Binding Declarations can also be used with arrays or types which implement a subset
|
||||||
|
* of the `std::tuple` interface. Those cases are not as interesting. We've always had the
|
||||||
|
* ability to inspect arrays via templates -- simple deduction operations work for that. And
|
||||||
|
* C++11's `std::tuple`s are already inspectable by their nature and types which implement a tuple-like
|
||||||
|
* interface are also easily inspected by pre-C++17 means.
|
||||||
|
*
|
||||||
|
* The most important thing C++17 Structured Binding brings to the language is the ability to
|
||||||
|
* (at compiletime) programmatically inspect any structure's members -- to learn their types,
|
||||||
|
* and with a bit of special effort, to learn their offsets. The names of those members are
|
||||||
|
* hidden, but their types are available, as is a way to work with all of them at once. Any
|
||||||
|
* Structured Binding is sufficient to do this -- one need only give a new name for each member of
|
||||||
|
* the type. `auto &[ a, b, c, d ]= someStruct;` is all that is needed and one has already performed
|
||||||
|
* an interesting feat of rudimentary reflection on the type `someStruct`. By loading those values
|
||||||
|
* into a tuple (by reference), by code such as `std::tie( a, b, c, d )`, a programmer can provide
|
||||||
|
* an anonymized, distilled reflection of the contents of that `struct`. This said, a library function
|
||||||
|
* which can decompose any `struct` into such a tie is very useful. `tuplizeAggregate` is exactly this.
|
||||||
|
*
|
||||||
|
* This function contains a pre-built set of such decompositions for structs of various sizes. C++17
|
||||||
|
* does not permit arbitrarily sized Structured Bindings, and so a limit had to be placed. The limit
|
||||||
|
* is fairly generous, however. If an aggregate size which is greater than the pre-build maximum is
|
||||||
|
* provided, then the compile will fail on a `static_assert` indicating this.
|
||||||
|
*
|
||||||
|
* Unfortunately, as a declaration syntax, the number of members in a `struct`'s body cannot be inferred
|
||||||
|
* through SFINAE by this means. Normally the user must explicitly provide the number of member
|
||||||
|
* variables. However, combined with a pair of C++11 features (based upon variadic templates and
|
||||||
|
* aggregate initialization syntax) we can infer the number of memmber values via a set of helper
|
||||||
|
* templates (which can also be called directly.)
|
||||||
|
*
|
||||||
|
* This kind of reflection into an aggregate type can prove very useful. Code generators for
|
||||||
|
* serialization, conversion tools, universal utility functions, and much more can all be built in
|
||||||
|
* C++17, today, using this kind of reflection! There's no need to wait for C++23 or beyond when
|
||||||
|
* static reflection is added to the language. A great deal of desired reflection use cases can be
|
||||||
|
* attained today. One just need write some code generators in terms of `std::tuple` and `std::tie`,
|
||||||
|
* then make any overloads (perhaps using ADL hooking tricks) which call `Alepha::Reflection::tuplizeAggregate`
|
||||||
|
* and pass that result to the general tuple form. For serializers and such, other techniques such as
|
||||||
|
* `boost::core::demangle( typeid( instance ).name() )` can be used to get nice names for types when
|
||||||
|
* implementing universal serializers. In fact, this can be used as a crutch for serializing more
|
||||||
|
* complicated user types (with private data and such). Those types can produce an aggregate "view"
|
||||||
|
* of what they must serialize or deserialize, and then they can hand that view off to such code
|
||||||
|
* generators. And, of cousre, this need not apply just to serialization.
|
||||||
|
*
|
||||||
|
* @param agg Aggregate instance to decompose into a `std::tie` based `std::tuple`.
|
||||||
|
* @tparam aggregate_size The number of members in the aggregate argument `agg`'s definition.
|
||||||
|
* @tparam Aggregate The type of the aggregate to decompose.
|
||||||
|
*/
|
||||||
|
// TODO: Make `aggregate_size` deduced via `Reflection::aggregate_ctor...` means.
|
||||||
|
template< std::size_t aggregate_size, typename Aggregate, typename= std::enable_if_t< not std::is_rvalue_reference_v< Aggregate > > >
|
||||||
|
constexpr decltype( auto )
|
||||||
|
tuplizeAggregate( Aggregate &&agg )
|
||||||
|
{
|
||||||
|
static_assert( std::is_aggregate_v< std::decay_t< Aggregate > >, "`tuplizeAggregate` only can be used on aggregates" );
|
||||||
|
|
||||||
|
// TODO: Generate these cases via boost preprocessor, to cut down on repetition...
|
||||||
|
if constexpr( aggregate_size == 0 ) return std::tuple{};
|
||||||
|
else if constexpr( aggregate_size == 1 )
|
||||||
|
{
|
||||||
|
auto &[ a0 ]= agg;
|
||||||
|
return std::tie( a0 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 2 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1 ]= agg;
|
||||||
|
return std::tie( a0, a1 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 3 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 4 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 5 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 6 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 7 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 8 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 9 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 10 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 11 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 12 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 13 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 14 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 15 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 );
|
||||||
|
}
|
||||||
|
else if constexpr( aggregate_size == 16 )
|
||||||
|
{
|
||||||
|
auto &[ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 ]= agg;
|
||||||
|
return std::tie( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 );
|
||||||
|
}
|
||||||
|
// Impossible, in this case -- we would have taken the original 0 branch were this so.
|
||||||
|
else static_assert( aggregate_size == 0, "The specified aggregate has more members than `tuplizeAggregate` can handle" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// This overload deduces the aggregate size using the initializer inspection utilities.
|
||||||
|
template< typename Aggregate >
|
||||||
|
constexpr decltype( auto )
|
||||||
|
tuplizeAggregate( Aggregate &&agg )
|
||||||
|
{
|
||||||
|
return tuplizeAggregate< compute_salient_members_count_v< std::decay_t< Aggregate > > >( std::forward< Aggregate >( agg ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename Aggregate >
|
||||||
|
using aggregate_tuple_t= decay_tuple_t< std::decay_t< decltype( tuplizeAggregate( std::declval< const Aggregate & >() ) ) > >;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Reflection::inline exports::inline tuplizeAggregate_m
|
||||||
|
{
|
||||||
|
using namespace detail::tuplizeAggregate_m::exports;
|
||||||
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace
|
|||||||
static_assert( Alepha::Reflection::aggregate_empty_bases_v< instance3 > == 2 );
|
static_assert( Alepha::Reflection::aggregate_empty_bases_v< instance3 > == 2 );
|
||||||
auto t= "test"_test <=[]
|
auto t= "test"_test <=[]
|
||||||
{
|
{
|
||||||
using namespace Alepha::Reflection::detail::aggregate_members;
|
using namespace Alepha::Reflection::detail::aggregate_members_m;
|
||||||
std::cout << Alepha::Reflection::aggregate_empty_bases_v< instance3 > << std::endl;
|
std::cout << Alepha::Reflection::aggregate_empty_bases_v< instance3 > << std::endl;
|
||||||
|
|
||||||
static_assert( std::is_empty_v< empty1 > );
|
static_assert( std::is_empty_v< empty1 > );
|
||||||
|
@ -41,7 +41,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m
|
|||||||
|
|
||||||
namespace C:: inline Colors
|
namespace C:: inline Colors
|
||||||
{
|
{
|
||||||
using namespace testing_colors::C:: Colors;
|
using namespace Testing::detail::colors_m::C:: Colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TestResult { Passed, Failed };
|
enum class TestResult { Passed, Failed };
|
||||||
@ -285,7 +285,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m
|
|||||||
}
|
}
|
||||||
|
|
||||||
using std::begin, std::end;
|
using std::begin, std::end;
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
|
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
|
||||||
auto
|
auto
|
||||||
|
@ -8,7 +8,7 @@ namespace
|
|||||||
{
|
{
|
||||||
namespace UnitTest= Alepha::Testing::exports;
|
namespace UnitTest= Alepha::Testing::exports;
|
||||||
|
|
||||||
using namespace Alepha::Utility::exports::evaluation_helpers;
|
using namespace Alepha::Utility::exports::evaluation_helpers_m;
|
||||||
using namespace UnitTest::literals;
|
using namespace UnitTest::literals;
|
||||||
using UnitTest::TableTest;
|
using UnitTest::TableTest;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using namespace Alepha::Testing::exports;
|
using namespace Alepha::Testing::exports;
|
||||||
using namespace Alepha::Utility::exports::evaluation_helpers;
|
using namespace Alepha::Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
int
|
int
|
||||||
add( int a, int b )
|
add( int a, int b )
|
||||||
|
@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Console.h>
|
#include <Alepha/Console.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Testing ::detail:: testing_colors
|
namespace Alepha::Hydrogen::Testing ::detail:: colors_m
|
||||||
{
|
{
|
||||||
namespace C
|
namespace C
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Testing::detail::testing
|
namespace Alepha::Hydrogen::Testing ::detail:: test_m
|
||||||
{
|
{
|
||||||
StaticValue< std::vector< std::tuple< std::string, bool, std::function< void() > > > > registry;
|
StaticValue< std::vector< std::tuple< std::string, bool, std::function< void() > > > > registry;
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
namespace Alepha::Hydrogen::Testing
|
namespace Alepha::Hydrogen::Testing
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace testing {} }
|
inline namespace exports { inline namespace test_m {} }
|
||||||
|
|
||||||
namespace detail::testing
|
namespace detail::test_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
inline namespace exports {}
|
||||||
|
|
||||||
@ -35,11 +35,11 @@ namespace Alepha::Hydrogen::Testing
|
|||||||
const bool debugTestRegistration= false or C::debug;
|
const bool debugTestRegistration= false or C::debug;
|
||||||
const bool debugTestRun= false or C::debug;
|
const bool debugTestRun= false or C::debug;
|
||||||
|
|
||||||
using namespace testing_colors::C::Colors;
|
using namespace detail::colors_m::C::Colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers_m;
|
||||||
using namespace Utility::exports::StaticValue_m;
|
using namespace Utility::exports::StaticValue_m;
|
||||||
|
|
||||||
struct TestName
|
struct TestName
|
||||||
@ -167,13 +167,13 @@ namespace Alepha::Hydrogen::Testing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace exports::testing
|
namespace exports::test_m
|
||||||
{
|
{
|
||||||
using namespace detail::testing::exports;
|
using namespace detail::test_m::exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace exports::inline literals::inline test_literals
|
namespace exports::inline literals::inline test_literals
|
||||||
{
|
{
|
||||||
using namespace detail::testing::exports::literals;
|
using namespace detail::test_m::exports::literals;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::detail::Thread_m
|
namespace Alepha::Hydrogen ::detail:: Thread_m
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility ::detail:: evaluation_helpers
|
namespace Alepha::Hydrogen::Utility ::detail:: evaluation_helpers_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -165,7 +165,7 @@ namespace Alepha::Hydrogen::Utility ::detail:: evaluation_helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility::inline exports::inline evaluation_helpers
|
namespace Alepha::Hydrogen::Utility::inline exports::inline evaluation_helpers_m
|
||||||
{
|
{
|
||||||
using namespace detail::evaluation_helpers::exports;
|
using namespace detail::evaluation_helpers_m::exports;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <boost/core/demangle.hpp>
|
#include <boost/core/demangle.hpp>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility::detail::fancyTypeName_m
|
namespace Alepha::Hydrogen::Utility ::detail:: fancyTypeName_m
|
||||||
{
|
{
|
||||||
std::string
|
std::string
|
||||||
exports::fancyTypeName( const std::type_index idx )
|
exports::fancyTypeName( const std::type_index idx )
|
||||||
|
@ -9,7 +9,7 @@ namespace
|
|||||||
using namespace Alepha::literals::option_literals;
|
using namespace Alepha::literals::option_literals;
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
using namespace Alepha::Utility::exports::evaluation_helpers;
|
using namespace Alepha::Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
int optionA= 42;
|
int optionA= 42;
|
||||||
std::optional< std::string > optionB;
|
std::optional< std::string > optionB;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
static auto init= Alepha::Utility::enroll <=[]
|
static auto init= Alepha::Utility::enroll <=[]
|
||||||
{
|
{
|
||||||
using namespace Alepha::Testing::exports;
|
using namespace Alepha::Testing::exports;
|
||||||
using namespace Alepha::Utility::exports::evaluation_helpers;
|
using namespace Alepha::Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
"A basic tuplization example"_test <=TableTest
|
"A basic tuplization example"_test <=TableTest
|
||||||
<
|
<
|
||||||
|
23
types.h
23
types.h
@ -4,21 +4,16 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen
|
namespace Alepha::Hydrogen ::detail:: types_m
|
||||||
{
|
{
|
||||||
inline namespace exports { inline namespace types_m {} }
|
inline namespace exports
|
||||||
|
|
||||||
namespace detail::types_m
|
|
||||||
{
|
{
|
||||||
inline namespace exports
|
using argvec_t= const char *const [];
|
||||||
{
|
using argcnt_t= int;
|
||||||
using argvec_t= const char *const [];
|
|
||||||
using argcnt_t= int;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace exports::types_m
|
|
||||||
{
|
|
||||||
using namespace detail::types_m::exports;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::inline exports::inline types_m
|
||||||
|
{
|
||||||
|
using namespace detail::types_m::exports;
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace Alepha::Hydrogen ::detail:: word_wrap_m
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers_m;
|
||||||
|
|
||||||
// Returns the number of chars in the line just written to.
|
// Returns the number of chars in the line just written to.
|
||||||
std::size_t
|
std::size_t
|
||||||
|
Reference in New Issue
Block a user