From f2ae99f648524100edd0d5d37a5e676ee76afdf0 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 25 Oct 2021 02:02:12 -0400 Subject: [PATCH] I added the lens-based comparison code. The metaprogramming is still based upon the angle-bracket type-form. Now to rewrite it in terms of the constexpr systems. --- Capabilities.h | 112 ++++++++++ comparisons.h | 415 ++++++++++++++++++++++++++++++++++++++ comparisons.test/0.cc | 85 ++++++++ comparisons.test/Makefile | 4 + 4 files changed, 616 insertions(+) create mode 100644 Capabilities.h create mode 100644 comparisons.h create mode 100644 comparisons.test/0.cc create mode 100644 comparisons.test/Makefile diff --git a/Capabilities.h b/Capabilities.h new file mode 100644 index 0000000..a1990b9 --- /dev/null +++ b/Capabilities.h @@ -0,0 +1,112 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#pragma once + +#include + +#include +#include + +namespace Alepha::Hydrogen +{ + inline namespace exports { inline namespace capabilities {} } + + namespace detail::capabilities + { + inline namespace exports + { + template< typename ... capabilities > + struct Capabilities; + + template< typename T, typename cap > + struct has_capability; //: std::is_base_of< cap, T > {}; + + +#if 0 + template< template< typename ... > class Type, typename ... Caps, typename cap > + struct has_capability< Type< Capabilities< Caps... > >, cap > + { + using T= Type< Capabilities< Caps... > >; + static constexpr bool value= Meta::find_in_tuple_v< cap, std::tuple< Caps... > > or std::is_base_of_v< cap, T >; + using type= has_capability; + }; + template< template< typename ... > class Type, typename ... Back, typename ... Caps, typename cap > + struct has_capability< Type< Capabilities< Caps... >, Back... >, cap > + { + using T= Type< Capabilities< Caps... >, Back... >; + static constexpr bool value= Meta::find_in_tuple_v< cap, std::tuple< Caps... > > or std::is_base_of_v< cap, T >; + using type= has_capability; + }; + + + template< template< typename ... > class Type, typename ... Front, typename ... Caps, typename cap > + struct has_capability< Type< Front..., Capabilities< Caps... > >, cap > + { + using T= Type< Front..., Capabilities< Caps... > >; + static constexpr bool value= Meta::find_in_tuple_v< cap, std::tuple< Caps... > > or std::is_base_of_v< cap, T >; + using type= has_capability; + }; +#endif + } + + template< typename T > + struct is_capability_list : std::false_type {}; + + template< typename ... Args > + struct is_capability_list< Capabilities< Args... > > : std::true_type {}; + + template< typename T > + constexpr bool is_capability_list_v= is_capability_list< T >::value; + + template< template< typename ... > class ... HigherKinds > + struct higher_kind_tuple {}; + + template< typename cap, typename ... Caps > + constexpr auto + has_cap( const Stud::type_identity< Capabilities< Caps... > > & ) + { + return Meta::find_if< Meta::bind1st< std::is_base_of, cap >, Meta::list< Caps... > >{}; + } + + template< typename cap > + constexpr std::false_type has_cap( const Meta::list<> & ) { return {}; } + + template< typename cap, typename First, typename ... TParams > + constexpr auto + has_cap( const Meta::list< First, TParams... > & ) + { + using depth_type= decltype( has_cap< cap >( Meta::list< TParams... >{} ) ); + if constexpr( is_capability_list_v< First > ) + { + using bool_type= decltype( has_cap< cap >( Stud::type_identity< First >() ) ); + if constexpr( bool_type::value ) + { + return std::bool_constant< bool_type::value >{}; + } + else return depth_type{}; + } + else return depth_type{}; + } + + template< typename cap, template< typename ... > class Class, typename ... TParams > + constexpr auto + has_cap( const Class< TParams... > & ) + { + return has_cap< cap >( Meta::list< TParams... >{} ); + } + + namespace exports + { + template< typename T, typename cap > + constexpr bool has_capability_v= std::is_base_of_v< cap, T > or decltype( has_cap< cap >( std::declval< T >() ) )::value; + + template< typename T, typename cap > + struct has_capability : std::bool_constant< has_capability_v< T, cap > > {}; + } + } + + namespace exports::capabilities + { + using namespace detail::capabilities::exports; + } +} diff --git a/comparisons.h b/comparisons.h new file mode 100644 index 0000000..b55b52e --- /dev/null +++ b/comparisons.h @@ -0,0 +1,415 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#pragma once + +#include + +#include + +#include +#include + + +namespace Alepha::Hydrogen +{ + inline namespace exports { inline namespace comparisons {} } + + namespace detail::comparisons + { + inline namespace exports {} + + using namespace Meta::exports::template_overload; + + // Basic capability support + // TODO: non-base-type capability via ADL rules on templates. + + struct comparable {}; + + namespace exports + { + using detail::comparisons::comparable; + } + + template< typename T > + constexpr bool has_comparable_capability_v= has_capability_v< std::decay_t< T >, comparable >; + + template< typename T > + struct has_comparable_capability : std::bool_constant< has_comparable_capability_v< T > > {}; + + + // Spaceship lens support + + template< typename T, typename= void > + struct has_spaceship_lens_member : std::false_type {}; + + template< typename T > + struct has_spaceship_lens_member< T, std::void_t< decltype( std::declval< const T & >().spaceship_lens() ) > > : std::true_type {}; + + template< typename T > + constexpr bool has_spaceship_lens_member_v= has_spaceship_lens_member< T >::value; + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< has_spaceship_lens_member_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + spaceship_lens( T &t ) + { + return t.spaceship_lens(); + } + + template< typename T, typename= void > + struct supports_spaceship_lens : std::false_type {}; + + template< typename T > + struct supports_spaceship_lens< T, std::void_t< decltype( spaceship_lens( std::declval< const T & >() ) ) > > : std::true_type {}; + + template< typename T > + constexpr bool supports_spaceship_lens_v= supports_spaceship_lens< T >::value; + + + // Value lens support + + template< typename T, typename= void > + struct has_value_lens_member : std::false_type {}; + + template< typename T > + struct has_value_lens_member< T, std::void_t< decltype( std::declval< const T & >().value_lens() ) > > : std::true_type {}; + + template< typename T > + constexpr bool has_value_lens_member_v= has_value_lens_member< T >::value; + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< has_value_lens_member_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + value_lens( T &t ) + { + return t.value_lens(); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_spaceship_lens_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + value_lens( T &t ) + { + return spaceship_lens( t ); + } + + template< typename T, typename= void > + struct supports_value_lens : std::false_type {}; + + template< typename T > + struct supports_value_lens< T, std::void_t< decltype( value_lens( std::declval< const T & >() ) ) > > : std::true_type {}; + + template< typename T > + constexpr bool supports_value_lens_v= supports_value_lens< T >::value; + + // Equality Lens support + + template< typename T, typename= void > + struct has_equality_lens_member : std::false_type {}; + + template< typename T > + struct has_equality_lens_member< T, std::void_t< decltype( std::declval< const T & >().equality_lens() ) > > : std::true_type {}; + + template< typename T > + constexpr bool has_equality_lens_member_v= has_equality_lens_member< T >::value; + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< has_equality_lens_member_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + equality_lens( T &t ) + { + return t.equality_lens(); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_value_lens_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + equality_lens( T &t ) + { + return value_lens( t ); + } + + template< typename T, typename= void > + struct supports_equality_lens : std::false_type {}; + + template< typename T > + struct supports_equality_lens< T, std::void_t< decltype( equality_lens( std::declval< const T & >() ) ) > > : std::true_type {}; + + template< typename T > + constexpr bool supports_equality_lens_v= supports_equality_lens< T >::value; + + + // Strict weak order lens support + + template< typename T, typename= void > + struct has_strict_weak_order_lens_member : std::false_type {}; + + template< typename T > + struct has_strict_weak_order_lens_member< T, std::void_t< decltype( std::declval< const T & >().strict_weak_order_lens() ) > > : std::true_type {}; + + template< typename T > + constexpr bool has_strict_weak_order_lens_member_v= has_strict_weak_order_lens_member< T >::value; + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< has_strict_weak_order_lens_member_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + strict_weak_order_lens( T &t ) + { + return t.strict_weak_order_lens(); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_value_lens_v< T > >, + overload< __LINE__ > = nullptr + > + decltype( auto ) + strict_weak_order_lens( T &t ) + { + return value_lens( t ); + } + + template< typename T, typename= void > + struct supports_strict_weak_order_lens : std::false_type {}; + + template< typename T > + struct supports_strict_weak_order_lens< T, std::void_t< decltype( strict_weak_order_lens( std::declval< const T & >() ) ) > > : std::true_type {}; + + template< typename T > + constexpr bool supports_strict_weak_order_lens_v= supports_strict_weak_order_lens< T >::value; + + + // Operator support: + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_equality_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator == ( const T &lhs, const T &rhs ) + { + return equality_lens( lhs ) == equality_lens( rhs ); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_equality_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator != ( const T &lhs, const T &rhs ) + { + return equality_lens( lhs ) != equality_lens( rhs ); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_strict_weak_order_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator < ( const T &lhs, const T &rhs ) + { + return strict_weak_order_lens( lhs ) < strict_weak_order_lens( rhs ); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_strict_weak_order_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator > ( const T &lhs, const T &rhs ) + { + return strict_weak_order_lens( lhs ) > strict_weak_order_lens( rhs ); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_strict_weak_order_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator <= ( const T &lhs, const T &rhs ) + { + return strict_weak_order_lens( lhs ) <= strict_weak_order_lens( rhs ); + } + + template + < + typename T, + typename= std::enable_if_t< has_comparable_capability_v< T > >, + typename= std::enable_if_t< supports_strict_weak_order_lens_v< T > >, + overload< __LINE__ > = nullptr + > + bool + operator >= ( const T &lhs, const T &rhs ) + { + return strict_weak_order_lens( lhs ) >= strict_weak_order_lens( rhs ); + } + + template< typename ... Args > + struct magma_hook + { + std::tuple< Args... > view; + }; + + namespace exports + { + template< typename ... Args > + auto + ordering_magma( Args && ... args ) + { + return magma_hook< Args... >{ std::tie( std::forward< Args >( args )... ) }; + } + } + + // TODO: Sort out the linear-trichotomous problem. + + template< typename comp, std::size_t index= 0, typename ... Args > + bool + compOp( const std::tuple< Args... > &lhs, const std::tuple< Args... > &rhs ) + { + if constexpr( index == sizeof...( Args ) ) return false; + else + { + const auto &l= std::get< index >( lhs ); + const auto &r= std::get< index >( rhs ); + if( comp{}( l, r ) ) return true; + else if( comp{}( r, l ) ) return false; + return compOp< comp, index + 1 >( lhs, rhs ); + } + } + + template< typename ... Args > + bool + operator < ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return compOp< std::less<> >( lhs.view, rhs.view ); + } + + template< typename ... Args > + bool + operator > ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return compOp< std::greater<> >( lhs.view, rhs.view ); + } + + + template< typename comp, std::size_t index= 0, typename ... Args > + bool + comp_eqOp( const std::tuple< Args... > &lhs, const std::tuple< Args... > &rhs ) + { + if constexpr( index == sizeof...( Args ) ) return true; + else + { + const auto &l= std::get< index >( lhs ); + const auto &r= std::get< index >( rhs ); + const bool first_pass= comp{}( l, r ); + if( first_pass and comp{}( r, l ) ) return comp_eqOp< comp, index + 1 >( lhs, rhs ); + return first_pass; + } + } + + template< typename ... Args > + bool + operator <= ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return comp_eqOp< std::less_equal<> >( lhs.view, rhs.view ); + } + + template< typename ... Args > + bool + operator >= ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return comp_eqOp< std::greater_equal<> >( lhs.view, rhs.view ); + } + + template< std::size_t index= 0, typename ... Args > + bool + eq( const std::tuple< Args... > &lhs, const std::tuple< Args... > &rhs ) + { + if constexpr( index == sizeof...( Args ) ) return true; + else + { + const auto &l= std::get< index >( lhs ); + const auto &r= std::get< index >( rhs ); + return ( l == r ) and eq< index + 1 >( lhs, rhs ); + } + } + + template< typename ... Args > + bool + operator == ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return eq( lhs.view, rhs.view ); + } + + template< std::size_t index= 0, typename ... Args > + bool + ne( const std::tuple< Args... > &lhs, const std::tuple< Args... > &rhs ) + { + if constexpr( index == sizeof...( Args ) ) return false; + else + { + const auto &l= std::get< index >( lhs ); + const auto &r= std::get< index >( rhs ); + return l != r and ne< index + 1 >( lhs, rhs ); + } + } + + template< typename ... Args > + bool + operator != ( const magma_hook< Args... > &lhs, const magma_hook< Args... > &rhs ) + { + return ne( lhs, rhs ); + } + } + + namespace exports::comparisons + { + using namespace detail::comparisons::exports; + } +} diff --git a/comparisons.test/0.cc b/comparisons.test/0.cc new file mode 100644 index 0000000..3a863e9 --- /dev/null +++ b/comparisons.test/0.cc @@ -0,0 +1,85 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#include + +#include +#include +#include + +namespace +{ + using Alepha::Testing::argcnt_t; + using Alepha::Testing::argvec_t; +} + +int +main( const argcnt_t argcnt, const argvec_t argvec ) +{ + return Alepha::Testing::runAllTests( argcnt, argvec ); +} + +namespace +{ + using namespace Alepha::Testing::exports; + using namespace Alepha::exports::comparisons; + using namespace Alepha::exports::capabilities; + + template< typename= int, typename= Capabilities< comparable >, typename= float, typename= Capabilities< short > > + struct Date_core + { + int y; + int m; + int d; + + auto value_lens() const { return std::tie( y, m, d ); } + }; + + using Date= Date_core<>; + static_assert( Alepha::has_capability_v< Date, comparable > ); + + template< template< typename > class op, typename T > + constexpr bool + comp( const T &lhs, const T &rhs ) + { + return op< T >{}( lhs, rhs ); + } + + auto tests= Alepha::Utility::enroll <=[] + { + "smoke.lt"_test <=TableTest< comp< std::less, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, true }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, false }, + }; + + "smoke.gt"_test <=TableTest< comp< std::greater, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, false }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, true }, + }; + + "smoke.le"_test <=TableTest< comp< std::less_equal, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, true }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, false }, + }; + + "smoke.ge"_test <=TableTest< comp< std::greater_equal, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, false }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, true }, + }; + + "smoke.eq"_test <=TableTest< comp< std::equal_to, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, false }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, false }, + }; + + "smoke.ne"_test <=TableTest< comp< std::not_equal_to, Date > >::Cases + { + { "smoke1", { { 1982, 12, 21 }, { 2020, 12, 15 } }, true }, + { "smoke1", { { 2020, 12, 15 }, { 1982, 12, 21 } }, true }, + }; + }; +} diff --git a/comparisons.test/Makefile b/comparisons.test/Makefile new file mode 100644 index 0000000..cd954b9 --- /dev/null +++ b/comparisons.test/Makefile @@ -0,0 +1,4 @@ +CXXFLAGS+= -std=c++17 -I ../ +CXXFLAGS+= -g -O0 + +all: 0