Skip to content

Commit

Permalink
Adding common_reference with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
whaeck committed Feb 14, 2024
1 parent e5ee470 commit 3fb63e2
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 2 deletions.
45 changes: 45 additions & 0 deletions src/tools/std20/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 > >;
Expand All @@ -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;
Expand All @@ -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<T1, T2> {};

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
Expand Down
187 changes: 187 additions & 0 deletions src/tools/std20/type_traits/details/common_reference.hpp
Original file line number Diff line number Diff line change
@@ -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<volatile T, U> {
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<X, Y>&, copy_cv_t<Y, X>&>;
};

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<const X&, Y&>;
};

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 <typename U> 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 <typename T, typename U>
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... > {};

}
5 changes: 3 additions & 2 deletions src/tools/std20/type_traits/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 )
87 changes: 87 additions & 0 deletions src/tools/std20/type_traits/test/common_reference.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// include Catch2
#include <catch2/catch_test_macros.hpp>

// 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

0 comments on commit 3fb63e2

Please sign in to comment.