diff --git a/src/tools/std20/type_traits.hpp b/src/tools/std20/type_traits.hpp index 6bbcb71..f27eda1 100644 --- a/src/tools/std20/type_traits.hpp +++ b/src/tools/std20/type_traits.hpp @@ -10,6 +10,8 @@ namespace njoy { namespace tools { namespace std20 { +// remove_cvref + template < typename T > struct remove_cvref { using type = std::remove_cv_t< std::remove_reference_t< T > >; @@ -18,6 +20,8 @@ struct remove_cvref { template < typename T > using remove_cvref_t = typename remove_cvref< T >::type; +// type_identity + template < typename T > struct type_identity { using type = T; @@ -26,6 +30,47 @@ struct type_identity { template < typename T > using type_identity_t = typename type_identity< T >::type; +// basic_common_reference + +template < typename T, typename U, template < typename > typename TQual, + template < typename > typename UQual> +struct basic_common_reference {}; + +// common_reference + +template < typename... > +struct common_reference; + +template < typename... Ts > +using common_reference_t = typename common_reference< Ts... >::type; + +#include "tools/std20/type_traits/details/common_reference.hpp" + +template <> +struct common_reference<> {}; + +template < typename T > +struct common_reference< T > { + + using type = T; +}; + +template < typename T1, typename T2 > +struct common_reference< T1, T2 > : detail::binary_common_ref {}; + +template < typename T1, typename T2, typename... Rest > +struct common_reference< T1, T2, Rest... > + : detail::multiple_common_reference< void, T1, T2, Rest... > {}; + +// is_bounded_array +// is_unbounded_array +// is_nothrow_convertible +// is_layout_compatible +// is_pointer_interconvertible_base_of +// is_pointer_interconvertible_with_class +// is_corresponding_member +// is_constant_evaluated + } // std20 namespace } // tools namespace } // njoy namespace diff --git a/src/tools/std20/type_traits/details/common_reference.hpp b/src/tools/std20/type_traits/details/common_reference.hpp new file mode 100644 index 0000000..08a5006 --- /dev/null +++ b/src/tools/std20/type_traits/details/common_reference.hpp @@ -0,0 +1,187 @@ +namespace detail { + +template < typename T, typename U > +struct copy_cv { + using type = U; +}; + +template < typename T, typename U > +struct copy_cv< const T, U > { + using type = std::add_const_t< U >; +}; + +template < typename T, typename U> +struct copy_cv { + using type = std::add_volatile_t< U >; +}; + +template < typename T, typename U > +struct copy_cv< const volatile T, U > { + using type = std::add_cv_t< U >; +}; + +template < typename T, typename U > +using copy_cv_t = typename copy_cv< T, U >::type; + +template < typename T, typename U > +using cond_res_t = decltype( false ? std::declval< T (&)()>()() + : std::declval< U (&)()>()() ); + +template < typename A, typename B, + typename X = std::remove_reference_t< A >, + typename Y = std::remove_reference_t< B >, + typename = void > +struct common_ref {}; + +template < typename A, typename B > +using common_ref_t = typename common_ref< A, B >::type; + +template < typename A, typename B, + typename X = std::remove_reference_t< A >, + typename Y = std::remove_reference_t< B >, + typename = void > +struct lval_common_ref {}; + +template < typename A, typename B, typename X, typename Y > +struct lval_common_ref< + A, B, X, Y, + std::enable_if_t< + std::is_reference_v< cond_res_t< copy_cv_t< X, Y >&, + copy_cv_t< Y, X >& > > > > { + + using type = cond_res_t&, copy_cv_t&>; +}; + +template < typename A, typename B > +using lval_common_ref_t = typename lval_common_ref< A, B >::type; + +template < typename A, typename B, typename X, typename Y > +struct common_ref< A&, B&, X, Y> : lval_common_ref< A&, B& > {}; + +template < typename X, typename Y > +using rref_cr_helper_t = std::remove_reference_t< lval_common_ref_t< X&, Y& > >&&; + +template < typename A, typename B, typename X, typename Y > +struct common_ref< + A&&, B&&, X, Y, + std::enable_if_t< + std::is_convertible_v< A&&, rref_cr_helper_t< X, Y > > && + std::is_convertible_v< B&&, rref_cr_helper_t< X, Y > > > > { + + using type = rref_cr_helper_t< X, Y >; +}; + +template < typename A, typename B, typename X, typename Y > +struct common_ref< + A&&, B&, X, Y, + std::enable_if_t< + std::is_convertible_v< A&&, lval_common_ref_t< const X&, Y& > > > > { + + using type = lval_common_ref_t; +}; + +template < typename A, typename B, typename X, typename Y > +struct common_ref< A&, B&&, X, Y > : common_ref< B&&, A& > {}; + +template < typename > +struct xref { + + template using type = U; +}; + +template < typename A > +struct xref< A& > { + + template < typename U > + using type = std::add_lvalue_reference_t< typename xref< A >::template type< U > >; +}; + +template < typename A > +struct xref< A&& > { + + template < typename U > + using type = std::add_rvalue_reference_t< typename xref< A >::template type< U > >; +}; + +template < typename A > +struct xref< const A > { + + template < typename U > + using type = std::add_const_t< typename xref< A >::template type< U > >; +}; + +template < typename A > +struct xref< volatile A > { + + template < typename U > + using type = std::add_volatile_t< typename xref< A >::template type< U > >; +}; + +template < typename A > +struct xref< const volatile A > { + + template < typename U > + using type = std::add_cv_t< typename xref< A >::template type< U > >; +}; + +template < template < typename... > typename AliasT, typename... Args > +auto exists_helper( long ) -> std::false_type; + +template < template < typename...> typename AliasT, typename... Args, + typename = AliasT< Args... > > +auto exists_helper(int) -> std::true_type; + +template < template < typename... > typename AliasT, typename... Args > +inline constexpr bool exists_v = decltype( exists_helper< AliasT, Args... >(0) )::value; + +template < typename T, typename U > +inline constexpr bool has_common_ref_v = exists_v< common_ref_t, T, U >; + +template +using basic_common_ref_t = + typename basic_common_reference< remove_cvref_t< T >, + remove_cvref_t< U >, + detail::xref< T >::template type, + detail::xref< U >::template type >::type; + +template < typename T, typename U > +inline constexpr bool has_basic_common_ref_v = exists_v< basic_common_ref_t, T, U >; + +template < typename T, typename U > +inline constexpr bool has_cond_res_v = exists_v< cond_res_t, T, U >; + +template < typename T, typename U, typename = void > +struct binary_common_ref : std::common_type< T, U > {}; + +template < typename T, typename U > +struct binary_common_ref< T, U, std::enable_if_t< has_common_ref_v< T, U > > > + : common_ref< T, U > {}; + +template < typename T, typename U > +struct binary_common_ref< + T, U, + std::enable_if_t< has_basic_common_ref_v< T, U > && + ! has_common_ref_v< T, U > > > { + + using type = basic_common_ref_t< T, U >; +}; + +template < typename T, typename U > +struct binary_common_ref< + T, U, + std::enable_if_t< has_cond_res_v< T, U > && + ! has_basic_common_ref_v< T, U > && + ! has_common_ref_v< T, U > > > { + + using type = cond_res_t< T, U >; +}; + +template < typename, typename T1, typename T2, typename... Rest > +struct multiple_common_reference {}; + +template < typename T1, typename T2, typename... Rest > +struct multiple_common_reference< + std::void_t< common_reference_t< T1, T2 > >, + T1, T2, Rest... > : common_reference< common_reference_t< T1, T2 >, Rest... > {}; + +} diff --git a/src/tools/std20/type_traits/test/CMakeLists.txt b/src/tools/std20/type_traits/test/CMakeLists.txt index ee75f44..e0c77fe 100644 --- a/src/tools/std20/type_traits/test/CMakeLists.txt +++ b/src/tools/std20/type_traits/test/CMakeLists.txt @@ -1,2 +1,3 @@ -add_cpp_test( std20.type_traits.remove_cvref remove_cvref.test.cpp ) -add_cpp_test( std20.type_traits.type_identity type_identity.test.cpp ) +add_cpp_test( std20.type_traits.remove_cvref remove_cvref.test.cpp ) +add_cpp_test( std20.type_traits.type_identity type_identity.test.cpp ) +add_cpp_test( std20.type_traits.common_reference common_reference.test.cpp ) diff --git a/src/tools/std20/type_traits/test/common_reference.test.cpp b/src/tools/std20/type_traits/test/common_reference.test.cpp new file mode 100644 index 0000000..2724ace --- /dev/null +++ b/src/tools/std20/type_traits/test/common_reference.test.cpp @@ -0,0 +1,87 @@ +// include Catch2 +#include + +// what we are testing +#include "tools/std20/type_traits.hpp" + +// other includes + +// convenience typedefs +using namespace njoy::tools; + +class Base {}; +class First : public Base {}; +class Second : public Base {}; + +SCENARIO( "common_reference and common_reference_t" ) { + + CHECK( std::is_same_v< std20::common_reference< int >::type, int > ); + CHECK( std::is_same_v< std20::common_reference< int&, int& >::type, int& > ); + CHECK( std::is_same_v< std20::common_reference< const int&, int& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< int&, const int& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< int&&, int& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< int&&, const int& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< int&, int&& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< const int&, int&& >::type, const int& > ); + CHECK( std::is_same_v< std20::common_reference< int&&, int&& >::type, int&& > ); + CHECK( std::is_same_v< std20::common_reference< const int&&, int&& >::type, const int&& > ); + CHECK( std::is_same_v< std20::common_reference< int&&, const int&& >::type, const int&& > ); + + CHECK( std::is_same_v< std20::common_reference< volatile int >::type, volatile int > ); + CHECK( std::is_same_v< std20::common_reference< volatile int&, int& >::type, volatile int& > ); + CHECK( std::is_same_v< std20::common_reference< volatile int&, const int& >::type, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference< const volatile int&, int& >::type, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference< int&, volatile int& >::type, volatile int& > ); + CHECK( std::is_same_v< std20::common_reference< const int&, volatile int& >::type, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference< int&, const volatile int& >::type, const volatile int& > ); + + CHECK( std::is_same_v< std20::common_reference< Base& >::type, Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, First& >::type, Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, Second& >::type, Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, First&, Second& >::type, Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, First&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, Second&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, First&&, Second&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, First&& >::type, const Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, First& >::type, const Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, First&, Second& >::type, Base& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, First&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, Second&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&&, First&&, Second&& >::type, Base&& > ); + CHECK( std::is_same_v< std20::common_reference< Base&, First&&, Second&& >::type, const Base& > ); + + CHECK( std::is_same_v< std20::common_reference_t< int >, int > ); + CHECK( std::is_same_v< std20::common_reference_t< int&, int& >, int& > ); + CHECK( std::is_same_v< std20::common_reference_t< const int&, int& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&, const int& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&&, int& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&&, const int& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&, int&& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< const int&, int&& >, const int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&&, int&& >, int&& > ); + CHECK( std::is_same_v< std20::common_reference_t< const int&&, int&& >, const int&& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&&, const int&& >, const int&& > ); + + CHECK( std::is_same_v< std20::common_reference_t< volatile int >, volatile int > ); + CHECK( std::is_same_v< std20::common_reference_t< volatile int&, int& >, volatile int& > ); + CHECK( std::is_same_v< std20::common_reference_t< volatile int&, const int& >, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference_t< const volatile int&, int& >, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&, volatile int& >, volatile int& > ); + CHECK( std::is_same_v< std20::common_reference_t< const int&, volatile int& >, const volatile int& > ); + CHECK( std::is_same_v< std20::common_reference_t< int&, const volatile int& >, const volatile int& > ); + + CHECK( std::is_same_v< std20::common_reference_t< Base& >, Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, First& >, Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, Second& >, Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, First&, Second& >, Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, First&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, Second&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, First&&, Second&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, First&& >, const Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, First& >, const Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, First&, Second& >, Base& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, First&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, Second&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&&, First&&, Second&& >, Base&& > ); + CHECK( std::is_same_v< std20::common_reference_t< Base&, First&&, Second&& >, const Base& > ); +} // SCENARIO