Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable binary comparison operations for internal tuple #1472

Merged
merged 33 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7daf437
Enable binary operators for tuple
danhoeflinger Mar 28, 2024
82acba3
Adding tuple unit tests
danhoeflinger Mar 29, 2024
186e45a
cleanup of unnecessary args, formatting
danhoeflinger Apr 5, 2024
8a87f85
add constexpr
danhoeflinger Apr 5, 2024
bac8c1f
adding operators for empty tuples
danhoeflinger Apr 5, 2024
eb41273
adding tests for compile time and empty tuples
danhoeflinger Apr 5, 2024
4d6dbb1
formatting
danhoeflinger Apr 5, 2024
c2e94df
implement operators to avoid copies in conversion
danhoeflinger Apr 8, 2024
496ad9c
move code and formatting
danhoeflinger Apr 8, 2024
f03cc8a
updated impl with no copies, conservative enable_if
danhoeflinger Apr 8, 2024
c5a5c56
removing unnecessary complexity
danhoeflinger Apr 8, 2024
2e7ba03
formatting
danhoeflinger Apr 8, 2024
209460a
remove variable name from fwd decl
danhoeflinger Apr 8, 2024
de3ae6e
improved enable_if
danhoeflinger Apr 8, 2024
b5cfd21
int for index
danhoeflinger Apr 8, 2024
be4a737
removing unused definition
danhoeflinger Apr 8, 2024
291d6b8
removing unused parameter names
danhoeflinger Aug 12, 2024
2a167c2
consolidating redundant instances, using forwarding types in helpers.
danhoeflinger Aug 12, 2024
fc66080
moving forward declaring of get functions
danhoeflinger Aug 12, 2024
3c91a36
consolidate logic for enabling op
danhoeflinger Aug 12, 2024
2e8b5e3
::std:: -> std::
danhoeflinger Aug 13, 2024
92e6c2f
spelling issues
danhoeflinger Aug 13, 2024
c03ccd9
spelling
danhoeflinger Aug 13, 2024
0690030
adding constexpr for ctors and some operations of internal tuple
danhoeflinger Aug 13, 2024
8922b8c
adress reviewer test feedback
danhoeflinger Aug 13, 2024
f23ca20
moving tuple_unit.pass
danhoeflinger Aug 13, 2024
ac84e97
renaming tuple unit tests
danhoeflinger Aug 13, 2024
d5b00e4
adding missing include
danhoeflinger Aug 13, 2024
91fdc30
removing warning-causing constexpr conversion operator
danhoeflinger Aug 13, 2024
dbd63ae
::std:: -> std::
danhoeflinger Aug 14, 2024
8e98b0f
fix assignment checks
danhoeflinger Aug 14, 2024
3c7d49c
remove constexpr checks from tests
danhoeflinger Aug 14, 2024
ef73c4f
Revert "adding constexpr for ctors and some operations of internal tu…
danhoeflinger Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 164 additions & 8 deletions include/oneapi/dpl/pstl/tuple_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ struct tuple_element<0, oneapi::dpl::__internal::tuple<T, Rest...>>
using type = T;
};

//forward declare new std::get<I>() functions so they can be used in impl
akukanov marked this conversation as resolved.
Show resolved Hide resolved
template <size_t _Idx, typename... _Tp>
constexpr std::tuple_element_t<_Idx, oneapi::dpl::__internal::tuple<_Tp...>>&
get(oneapi::dpl::__internal::tuple<_Tp...>&);

template <size_t _Idx, typename... _Tp>
constexpr std::tuple_element_t<_Idx, oneapi::dpl::__internal::tuple<_Tp...>> const&
get(const oneapi::dpl::__internal::tuple<_Tp...>&);

template <size_t _Idx, typename... _Tp>
constexpr std::tuple_element_t<_Idx, oneapi::dpl::__internal::tuple<_Tp...>>&&
get(oneapi::dpl::__internal::tuple<_Tp...>&&);

template <size_t _Idx, typename... _Tp>
constexpr std::tuple_element_t<_Idx, oneapi::dpl::__internal::tuple<_Tp...>> const&&
get(const oneapi::dpl::__internal::tuple<_Tp...>&&);

template <typename... Args>
struct tuple_size<oneapi::dpl::__internal::tuple<Args...>> : ::std::integral_constant<::std::size_t, sizeof...(Args)>
{
Expand Down Expand Up @@ -289,6 +306,82 @@ struct __copy_assignable_holder<_Tp, false> : oneapi::dpl::__internal::__value_h
operator=(__copy_assignable_holder&& other) = default;
};

template <typename _Tuple1, typename _Tuple2, int I = 0>
constexpr bool
__equal(_Tuple1&& __lhs, _Tuple2&& __rhs)
{
static_assert(std::tuple_size_v<std::decay_t<_Tuple1>> == std::tuple_size_v<std::decay_t<_Tuple2>>,
"Tuples must have the same size to be equality compared");
if constexpr (I < std::tuple_size_v<std::decay_t<_Tuple1>>)
{
return std::get<I>(__lhs) == std::get<I>(__rhs) &&
oneapi::dpl::__internal::__equal<_Tuple1, _Tuple2, I + 1>(std::forward<_Tuple1>(__lhs),
std::forward<_Tuple2>(__rhs));
}
else
{
return true;
}
}
template <typename _Tuple1, typename _Tuple2, int I = 0>
constexpr bool
__less(_Tuple1&& __lhs, _Tuple2&& __rhs)
{
static_assert(std::tuple_size_v<std::decay_t<_Tuple1>> == std::tuple_size_v<std::decay_t<_Tuple2>>,
"Tuples must have the same size to be compared");
if constexpr (I < std::tuple_size_v<std::decay_t<_Tuple1>>)
{
return std::get<I>(__lhs) < std::get<I>(__rhs) ||
(!(std::get<I>(__rhs) < std::get<I>(__lhs)) &&
oneapi::dpl::__internal::__less<_Tuple1, _Tuple2, I + 1>(std::forward<_Tuple1>(__lhs),
std::forward<_Tuple2>(__rhs)));
}
else
{
return false;
}
}

template <typename T>
struct __is_internal_tuple : std::false_type
{
};

template <typename... Ts>
struct __is_internal_tuple<oneapi::dpl::__internal::tuple<Ts...>> : std::true_type
{
};

template <typename T>
inline constexpr bool __is_internal_tuple_v = __is_internal_tuple<T>::value;

template <typename T>
struct __is_std_tuple : std::false_type
{
};

template <typename... Ts>
struct __is_std_tuple<std::tuple<Ts...>> : std::true_type
{
};

template <typename T>
inline constexpr bool __is_std_tuple_v = __is_std_tuple<T>::value;

// Either LHS = this internal tuple type + RHS = any compatible tuple type
// or LHS = std::tuple + RHS = this internal tuple type
// Note: We do not allow LHS to be "any compatible type" with a RHS as this internal tuple type because then we would
// have ambiguity comparing two different compatible internal tuple types, because both instantiations would
// create the necessary comparison. As written, the type instantiation of the type in the LHS is always
// responsible for creating this comparison operator.
template <typename _ThisTuple, typename _U, typename _V>
inline constexpr bool __enable_comparison_op_v =
SergeyKopienko marked this conversation as resolved.
Show resolved Hide resolved
(std::is_same_v<std::decay_t<_ThisTuple>, std::decay_t<_U>> && //LHS +
(oneapi::dpl::__internal::__is_std_tuple_v<std::decay_t<_V>> || //RHS option 1
oneapi::dpl::__internal::__is_internal_tuple_v<std::decay_t<_V>>)) || //RHS option 2 - OR -
(oneapi::dpl::__internal::__is_std_tuple_v<std::decay_t<_U>> && //LHS +
std::is_same_v<std::decay_t<_ThisTuple>, std::decay_t<_V>>); //RHS

template <typename T1, typename... T>
struct tuple<T1, T...>
{
Expand Down Expand Up @@ -424,15 +517,52 @@ struct tuple<T1, T...>
return *this;
}

friend bool
operator==(const tuple& __lhs, const tuple& __rhs)
template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator==(_U&& __lhs, _V&& __rhs)
{
return oneapi::dpl::__internal::__equal(std::forward<_U>(__lhs), std::forward<_V>(__rhs));
}

template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator!=(_U&& __lhs, _V&& __rhs)
{
return !oneapi::dpl::__internal::__equal(std::forward<_U>(__lhs), std::forward<_V>(__rhs));
}

template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator<(_U&& __lhs, _V&& __rhs)
{
return oneapi::dpl::__internal::__less(std::forward<_U>(__lhs), std::forward<_V>(__rhs));
}

template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator<=(_U&& __lhs, _V&& __rhs)
{
return __lhs.holder.value == __rhs.holder.value && __lhs.next == __rhs.next;
return !oneapi::dpl::__internal::__less(std::forward<_V>(__rhs), std::forward<_U>(__lhs));
}
friend bool
operator!=(const tuple& __lhs, const tuple& __rhs)

template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator>(_U&& __lhs, _V&& __rhs)
{
return oneapi::dpl::__internal::__less(std::forward<_V>(__rhs), std::forward<_U>(__lhs));
}

template <typename _U, typename _V,
std::enable_if_t<oneapi::dpl::__internal::__enable_comparison_op_v<tuple, _U, _V>, int> = 0>
friend constexpr bool
operator>=(_U&& __lhs, _V&& __rhs)
{
return !(__lhs == __rhs);
return !oneapi::dpl::__internal::__less(std::forward<_U>(__lhs), std::forward<_V>(__rhs));
}

template <typename U1, typename... U, ::std::size_t... _Ip>
Expand All @@ -456,15 +586,41 @@ struct tuple<>

tuple operator[](tuple) { return {}; }
tuple operator[](const tuple&) const { return {}; }
operator tuple_type() const { return tuple_type{}; }
tuple&
operator=(const tuple&) = default;
tuple&
operator=(const ::std::tuple<>& /*other*/)
{
return *this;
}
friend bool
operator==(const tuple& /*__lhs*/, const tuple& /*__rhs*/)
friend constexpr bool
operator==(const tuple&, const tuple&)
{
return true;
}
friend constexpr bool
operator!=(const tuple&, const tuple&)
{
return false;
}
friend constexpr bool
operator<(const tuple&, const tuple&)
{
return false;
}
friend constexpr bool
operator<=(const tuple&, const tuple&)
{
return true;
}
friend constexpr bool
operator>(const tuple&, const tuple&)
{
return false;
}
friend constexpr bool
operator>=(const tuple&, const tuple&)
{
return true;
}
Expand Down
98 changes: 98 additions & 0 deletions test/general/implementation_details/internal_tuple.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// -*- C++ -*-
//===-- tuple_unit.pass.cpp -----------------------------------------------===//
//
// Copyright (C) Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// This file incorporates work covered by the following copyright and permission
// notice:
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
//
//===----------------------------------------------------------------------===//
#include <cstdint>
#include "support/test_config.h"

#include _PSTL_TEST_HEADER(tuple)

#include "support/utils.h"

template <typename... _T>
static oneapi::dpl::__internal::tuple<_T...>
to_onedpl_tuple(const std::tuple<_T...>& __t)
{
return oneapi::dpl::__internal::tuple<_T...>(__t);
}

template <typename Tuple1, typename Tuple2>
void
test_tuple(Tuple1 t1, Tuple2 t2)
{

auto onedpl_t1 = to_onedpl_tuple(t1);
auto onedpl_t2 = to_onedpl_tuple(t2);

static_assert(std::is_trivially_copyable_v<decltype(onedpl_t1)>, "oneDPL tuple is not trivially copyable");

// Test binary comparison operators for std::tuple and oneAPI::dpl::__internal::tuple
EXPECT_EQ((t1 == t2), (onedpl_t1 == onedpl_t2), "equality comparison does not match std::tuple");
EXPECT_EQ((t1 != t2), (onedpl_t1 != onedpl_t2), "inquality comparison does not match std::tuple");
EXPECT_EQ((t1 < t2), (onedpl_t1 < onedpl_t2), "less than comparison does not match std::tuple");
EXPECT_EQ((t1 <= t2), (onedpl_t1 <= onedpl_t2), "less than or equal to comparison does not match std::tuple");
EXPECT_EQ((t1 > t2), (onedpl_t1 > onedpl_t2), "greater than comparison does not match std::tuple");
EXPECT_EQ((t1 >= t2), (onedpl_t1 >= onedpl_t2), "greater than or equal to comparison does not match std::tuple");

EXPECT_EQ((t1 == t2), (t1 == onedpl_t2), "equality comparison does not match std::tuple");
EXPECT_EQ((t1 != t2), (t1 != onedpl_t2), "inquality comparison does not match std::tuple");
EXPECT_EQ((t1 < t2), (t1 < onedpl_t2), "less than comparison does not match std::tuple");
EXPECT_EQ((t1 <= t2), (t1 <= onedpl_t2), "less than or equal to comparison does not match std::tuple");
EXPECT_EQ((t1 > t2), (t1 > onedpl_t2), "greater than comparison does not match std::tuple");
EXPECT_EQ((t1 >= t2), (t1 >= onedpl_t2), "greater than or equal to comparison does not match std::tuple");

EXPECT_EQ((t1 == t2), (onedpl_t1 == t2), "equality comparison does not match std::tuple");
EXPECT_EQ((t1 != t2), (onedpl_t1 != t2), "inquality comparison does not match std::tuple");
EXPECT_EQ((t1 < t2), (onedpl_t1 < t2), "less than comparison does not match std::tuple");
EXPECT_EQ((t1 <= t2), (onedpl_t1 <= t2), "less than or equal to comparison does not match std::tuple");
EXPECT_EQ((t1 > t2), (onedpl_t1 > t2), "greater than comparison does not match std::tuple");
EXPECT_EQ((t1 >= t2), (onedpl_t1 >= t2), "greater than or equal to comparison does not match std::tuple");

//checking default construction (assumes elements are default constructible)
decltype(onedpl_t1) onedpl_t3{};
decltype(onedpl_t2) onedpl_t4{};
decltype(t1) t3{};
decltype(t2) t4{};

t4 = onedpl_t1;
EXPECT_TRUE(t1 == t4, "assignment of oneDPL tuple to std::tuple provides incorrect results");

t3 = onedpl_t2;
EXPECT_TRUE(t2 == t3, "assignment of oneDPL tuple to std::tuple provides incorrect results");

onedpl_t3 = t2;
EXPECT_TRUE(onedpl_t2 == onedpl_t3, "assignment of oneDPL tuple from std::tuple provides incorrect results");

onedpl_t4 = t1;
EXPECT_TRUE(onedpl_t1 == onedpl_t4, "assignment of oneDPL tuple from std::tuple provides incorrect results");

decltype(onedpl_t1) onedpl_t5 = onedpl_t2;
decltype(onedpl_t1) onedpl_t6 = onedpl_t1;

swap(onedpl_t5, onedpl_t6);
EXPECT_TRUE(((onedpl_t1 == onedpl_t5) && (onedpl_t6 == onedpl_t2)),
"swap of oneDPL tuple provides incorrect results");
}

int
main()
{
test_tuple(std::tuple<int, int, int>{1, 2, 3}, std::tuple<int, int, int>{1, 2, 3});
test_tuple(std::tuple<int, int, int>{1, 2, 3}, std::tuple<int, int, int>{1, 2, 4});
test_tuple(std::tuple<int, int, int>{1, 2, 3}, std::tuple<int, int, int>{0, 2, 4});
test_tuple(std::tuple<int, int, std::uint64_t>{1, 2, 3}, std::tuple<std::uint32_t, int, int>{1, 2, 3});
test_tuple(std::tuple<int, int, std::uint64_t>{1, 2, 3}, std::tuple<std::uint32_t, int, int>{0, 2, 4});
test_tuple(std::tuple<>{}, std::tuple<>{});

return TestUtils::done();
}
Loading