Skip to content

Commit ae66581

Browse files
committed
Update the tuple
- update the tuple to c++20 - fix clang-tidy warnings - do not use CTAD (as nvcc fails with single argument ctors)
1 parent e9579bb commit ae66581

10 files changed

Lines changed: 1431 additions & 1810 deletions

File tree

Lines changed: 110 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// NOLINTBEGIN(readability-identifier-naming)
12
// SPDX-FileCopyrightText: 2026 CExA-project
23
// SPDX-License-Identifier: MIT or Apache-2.0 with LLVM-exception
34
#pragma once
@@ -9,6 +10,10 @@
910
#include "traits.hpp"
1011
#include "tuple.hpp"
1112

13+
#ifdef CEXA_HAS_CXX23
14+
# include <functional>
15+
#endif
16+
1217
namespace cexa {
1318

1419
namespace impl {
@@ -18,102 +23,137 @@ template <class U>
1823
constexpr bool is_reference_wrapper_v<std::reference_wrapper<U>> = true;
1924

2025
template <class C, class Pointed, class Object, class... Args>
21-
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) invoke_ptr(Pointed C::* member,
22-
Object&& object,
23-
Args&&... args) {
24-
using object_t = remove_cvref_t<Object>;
25-
constexpr bool is_wrapped = is_reference_wrapper_v<object_t>;
26-
constexpr bool is_derived_object =
27-
std::is_same_v<C, object_t> || std::is_base_of_v<C, object_t>;
28-
29-
if constexpr (std::is_function_v<Pointed>) {
30-
if constexpr (is_derived_object) {
31-
return (std::forward<Object>(object).*
32-
member)(std::forward<Args>(args)...);
33-
} else if constexpr (is_wrapped) {
34-
return (object.get().*member)(std::forward<Args>(args)...);
35-
} else {
36-
return ((*std::forward<Object>(object)).*
37-
member)(std::forward<Args>(args)...);
38-
}
39-
} else {
40-
static_assert(std::is_object_v<Pointed> && sizeof...(args) == 0);
41-
if constexpr (is_derived_object) {
42-
return std::forward<Object>(object).*member;
43-
} else if constexpr (is_wrapped) {
44-
return object.get().*member;
26+
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) invoke_ptr(
27+
Pointed C::*member,
28+
Object&& object,
29+
Args&&... args)
30+
{
31+
using object_t = std::remove_cvref_t<Object>;
32+
constexpr bool is_wrapped = is_reference_wrapper_v<object_t>;
33+
constexpr bool is_derived_object
34+
= std::is_same_v<C, object_t> || std::is_base_of_v<C, object_t>;
35+
36+
if constexpr (std::is_function_v<Pointed>) {
37+
if constexpr (is_derived_object) {
38+
return (std::forward<Object>(object).*member)(std::forward<Args>(args)...);
39+
} else if constexpr (is_wrapped) {
40+
return (object.get().*member)(std::forward<Args>(args)...);
41+
} else {
42+
return ((*std::forward<Object>(object)).*member)(std::forward<Args>(args)...);
43+
}
4544
} else {
46-
return (*std::forward<Object>(object)).*member;
45+
static_assert(std::is_object_v<Pointed> && sizeof...(args) == 0);
46+
if constexpr (is_derived_object) {
47+
return std::forward<Object>(object).*member;
48+
} else if constexpr (is_wrapped) {
49+
return object.get().*member;
50+
} else {
51+
return (*std::forward<Object>(object)).*member;
52+
}
4753
}
48-
}
4954
}
5055

56+
// f may be a host function
57+
CEXA_NVCC_HOST_DEVICE_CHECK_DISABLE
5158
template <class F, class... Args>
52-
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) invoke(F&& f, Args&&... args) {
53-
if constexpr (std::is_member_pointer_v<remove_cvref_t<F>>) {
54-
return invoke_ptr(f, std::forward<Args>(args)...);
55-
} else {
56-
return (std::forward<F>(f))(std::forward<Args>(args)...);
57-
}
59+
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) invoke(F&& f, Args&&... args)
60+
{
61+
if constexpr (std::is_member_pointer_v<std::remove_cvref_t<F>>) {
62+
return invoke_ptr(f, std::forward<Args>(args)...);
63+
} else {
64+
return (std::forward<F>(f))(std::forward<Args>(args)...);
65+
}
5866
}
5967

6068
template <class F, class Tuple, std::size_t... I>
61-
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(
62-
F&& f, Tuple&& t, std::index_sequence<I...>) {
63-
return invoke(std::forward<F>(f), cexa::get<I>(std::forward<Tuple>(t))...);
69+
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(F&& f, Tuple&& t, std::index_sequence<I...>)
70+
{
71+
return invoke(std::forward<F>(f), cexa::get<I>(std::forward<Tuple>(t))...);
6472
}
6573

74+
// This might call a host only constructor
75+
CEXA_NVCC_HOST_DEVICE_CHECK_DISABLE
6676
template <class T, class Tuple, std::size_t... I>
67-
KOKKOS_INLINE_FUNCTION constexpr T make_from_tuple(Tuple&& t,
68-
std::index_sequence<I...>) {
69-
return T(cexa::get<I>(std::forward<Tuple>(t))...);
77+
KOKKOS_INLINE_FUNCTION constexpr T make_from_tuple(Tuple&& t, std::index_sequence<I...>)
78+
{
79+
return T(cexa::get<I>(std::forward<Tuple>(t))...);
7080
}
7181

72-
template <class U, class T, std::size_t = tuple_size_v<impl::remove_cvref_t<T>>>
73-
struct make_tuple_constraint : std::true_type {};
82+
template <class U, class T, std::size_t = tuple_size_v<std::remove_cvref_t<T>>>
83+
struct make_tuple_constraint : std::true_type
84+
{
85+
};
7486

7587
template <class U, class Tuple>
76-
struct make_tuple_constraint<U, Tuple, 1> {
77-
static constexpr bool value = !impl::reference_constructs_from_temporary_v<
78-
U, decltype(get<0>(std::declval<Tuple>()))>;
88+
struct make_tuple_constraint<U, Tuple, 1>
89+
{
90+
static constexpr bool value = !impl::reference_constructs_from_temporary_v<
91+
U,
92+
decltype(get<0>(std::declval<Tuple>()))>;
7993
};
8094

81-
template <class T, class Tuple, class seq>
95+
template <class T, class Tuple, class Seq>
8296
struct is_constructible_from_tuple;
8397

8498
template <class T, class Tuple, std::size_t... Ints>
85-
struct is_constructible_from_tuple<T, Tuple, std::index_sequence<Ints...>> {
86-
static constexpr bool value =
87-
std::is_constructible_v<T, decltype(get<Ints>(std::declval<Tuple>()))...>;
99+
struct is_constructible_from_tuple<T, Tuple, std::index_sequence<Ints...>>
100+
{
101+
static constexpr bool value
102+
= std::is_constructible_v<T, decltype(get<Ints>(std::declval<Tuple>()))...>;
88103
};
89104

90105
template <class T, class Tuple>
91-
inline constexpr bool is_constructible_from_tuple_v =
92-
is_constructible_from_tuple<T, Tuple,
93-
std::make_index_sequence<tuple_size_v<
94-
impl::remove_cvref_t<Tuple>>>>::value;
106+
inline constexpr bool is_constructible_from_tuple_v = is_constructible_from_tuple<
107+
T,
108+
Tuple,
109+
std::make_index_sequence<tuple_size_v<std::remove_cvref_t<Tuple>>>>::value;
110+
111+
#ifdef CEXA_HAS_CXX23
112+
template <class F, class Tuple, class Other>
113+
struct is_nothrow_applicable : std::false_type
114+
{
115+
};
95116

96-
} // namespace impl
117+
template <class F, class Tuple, std::size_t... Is>
118+
struct is_nothrow_applicable<F, Tuple, std::index_sequence<Is...>>
119+
{
120+
static constexpr bool value
121+
= noexcept(std::invoke(std::declval<F>(), get<Is>(std::declval<Tuple>())...));
122+
};
97123

98124
template <class F, class Tuple>
99-
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(F&& f, Tuple&& t) {
100-
static_assert(impl::is_tuple_v<impl::remove_cvref_t<Tuple>>,
101-
"cexa::apply can only be called with cexa::tuple");
102-
return impl::apply(
103-
std::forward<F>(f), std::forward<Tuple>(t),
104-
std::make_index_sequence<tuple_size_v<std::remove_reference_t<Tuple>>>{});
125+
constexpr bool is_nothrow_applicable_v = is_nothrow_applicable<
126+
F,
127+
Tuple,
128+
decltype(std::make_index_sequence<tuple_size_v<std::remove_cvref_t<Tuple>>> {})>::value;
129+
#endif
130+
} // namespace impl
131+
132+
template <class F, class Tuple>
133+
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(F&& f, Tuple&& t)
134+
#ifdef CEXA_HAS_CXX23
135+
noexcept(impl::is_nothrow_applicable_v<F, Tuple>)
136+
#endif
137+
{
138+
static_assert(
139+
impl::is_tuple_v<std::remove_cvref_t<Tuple>>,
140+
"cexa::apply can only be called with cexa::tuple");
141+
return impl::
142+
apply(std::forward<F>(f),
143+
std::forward<Tuple>(t),
144+
std::make_index_sequence<tuple_size_v<std::remove_reference_t<Tuple>>> {});
105145
}
106146

107-
template <
108-
class T, class Tuple,
109-
class = std::enable_if_t<impl::is_constructible_from_tuple_v<T, Tuple>>>
110-
KOKKOS_INLINE_FUNCTION constexpr T make_from_tuple(Tuple&& t) {
111-
static_assert(impl::is_tuple_v<impl::remove_cvref_t<Tuple>>,
112-
"cexa::make_from_tuple can only be called with cexa::tuple");
113-
constexpr std::size_t size = tuple_size_v<std::remove_reference_t<Tuple>>;
114-
static_assert(
115-
impl::make_tuple_constraint<T, impl::remove_cvref_t<Tuple>>::value);
116-
return impl::make_from_tuple<T>(std::forward<Tuple>(t),
117-
std::make_index_sequence<size>{});
147+
template <class T, class Tuple>
148+
requires impl::is_constructible_from_tuple_v<T, Tuple>
149+
KOKKOS_INLINE_FUNCTION constexpr T make_from_tuple(Tuple&& t)
150+
{
151+
static_assert(
152+
impl::is_tuple_v<std::remove_cvref_t<Tuple>>,
153+
"cexa::make_from_tuple can only be called with cexa::tuple");
154+
constexpr std::size_t size = tuple_size_v<std::remove_reference_t<Tuple>>;
155+
static_assert(impl::make_tuple_constraint<T, std::remove_cvref_t<Tuple>>::value);
156+
return impl::make_from_tuple<T>(std::forward<Tuple>(t), std::make_index_sequence<size> {});
118157
}
119-
} // namespace cexa
158+
} // namespace cexa
159+
// NOLINTEND(readability-identifier-naming)
Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
1+
// NOLINTBEGIN(readability-identifier-naming)
12
// SPDX-FileCopyrightText: 2026 CExA-project
23
// SPDX-License-Identifier: MIT or Apache-2.0 with LLVM-exception
34
#pragma once
45

56
#include <type_traits>
67
#include <utility>
78

8-
#include "tuple.hpp"
9-
#include "traits.hpp"
109
#include "helper.hpp"
10+
#include "traits.hpp"
11+
#include "tuple.hpp"
1112

1213
namespace cexa {
1314

1415
// tuple.creation
1516
template <class... TTypes>
16-
KOKKOS_INLINE_FUNCTION constexpr tuple<impl::unwrap_ref_decay_t<TTypes>...>
17-
make_tuple(TTypes&&... t) {
18-
return {std::forward<TTypes>(t)...};
17+
KOKKOS_INLINE_FUNCTION constexpr tuple<std::unwrap_ref_decay_t<TTypes>...> make_tuple(TTypes&&... t)
18+
{
19+
return {std::forward<TTypes>(t)...};
1920
}
2021

2122
template <class... TTypes>
22-
KOKKOS_INLINE_FUNCTION constexpr tuple<TTypes&&...> forward_as_tuple(
23-
TTypes&&... t) noexcept {
24-
return tuple<TTypes&&...>(std::forward<TTypes>(t)...);
23+
KOKKOS_INLINE_FUNCTION constexpr tuple<TTypes&&...> forward_as_tuple(TTypes&&... t) noexcept
24+
{
25+
return tuple<TTypes&&...>(std::forward<TTypes>(t)...);
2526
}
2627

2728
template <class... TTypes>
28-
KOKKOS_INLINE_FUNCTION constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept {
29-
return tuple<TTypes&...>{t...};
29+
KOKKOS_INLINE_FUNCTION constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept
30+
{
31+
return tuple<TTypes&...> {t...};
3032
}
3133

3234
// tuple_cat helper
@@ -35,53 +37,82 @@ template <std::size_t A, class B, class C, class... D>
3537
struct cartesian_product_impl;
3638

3739
template <std::size_t I, std::size_t... Res1, std::size_t... Res2>
38-
struct cartesian_product_impl<I, std::index_sequence<Res1...>,
39-
std::index_sequence<Res2...>> {
40-
using seq1 = std::index_sequence<Res1...>;
41-
using seq2 = std::index_sequence<Res2...>;
40+
struct cartesian_product_impl<I, std::index_sequence<Res1...>, std::index_sequence<Res2...>>
41+
{
42+
using seq1 = std::index_sequence<Res1...>;
43+
using seq2 = std::index_sequence<Res2...>;
4244
};
4345

44-
template <std::size_t I, std::size_t... Res1, std::size_t... Res2,
45-
class... RemainingSeq>
46-
struct cartesian_product_impl<I, std::index_sequence<Res1...>,
47-
std::index_sequence<Res2...>,
48-
std::index_sequence<>, RemainingSeq...>
49-
: cartesian_product_impl<I + 1, std::index_sequence<Res1...>,
50-
std::index_sequence<Res2...>, RemainingSeq...> {};
46+
template <std::size_t I, std::size_t... Res1, std::size_t... Res2, class... RemainingSeq>
47+
struct cartesian_product_impl<
48+
I,
49+
std::index_sequence<Res1...>,
50+
std::index_sequence<Res2...>,
51+
std::index_sequence<>,
52+
RemainingSeq...>
53+
: cartesian_product_impl<
54+
I + 1,
55+
std::index_sequence<Res1...>,
56+
std::index_sequence<Res2...>,
57+
RemainingSeq...>
58+
{
59+
};
5160

52-
template <std::size_t I, std::size_t... Res1, std::size_t... Res2,
53-
std::size_t Head, std::size_t... Tail, class... RemainingSeq>
61+
template <
62+
std::size_t I,
63+
std::size_t... Res1,
64+
std::size_t... Res2,
65+
std::size_t Head,
66+
std::size_t... Tail,
67+
class... RemainingSeq>
5468
struct cartesian_product_impl<
55-
I, std::index_sequence<Res1...>, std::index_sequence<Res2...>,
56-
std::index_sequence<Head, Tail...>, RemainingSeq...>
69+
I,
70+
std::index_sequence<Res1...>,
71+
std::index_sequence<Res2...>,
72+
std::index_sequence<Head, Tail...>,
73+
RemainingSeq...>
5774
: cartesian_product_impl<
58-
I + 1, std::index_sequence<Res1..., Head, Tail...>,
59-
std::index_sequence<Res2..., I, ((void)Tail, I)...>,
60-
RemainingSeq...> {};
75+
I + 1,
76+
std::index_sequence<Res1..., Head, Tail...>,
77+
std::index_sequence<Res2..., I, ((void)Tail, I)...>,
78+
RemainingSeq...>
79+
{
80+
};
6181

6282
template <class... Tuples>
6383
struct cartesian_product
64-
: cartesian_product_impl<0, std::index_sequence<>, std::index_sequence<>,
65-
std::make_index_sequence<tuple_size<
66-
std::remove_reference_t<Tuples>>::value>...> {
84+
: cartesian_product_impl<
85+
0,
86+
std::index_sequence<>,
87+
std::index_sequence<>,
88+
std::make_index_sequence<tuple_size<std::remove_reference_t<Tuples>>::value>...>
89+
{
6790
};
6891

92+
// We might call std::get depending on the types in Tuples
93+
CEXA_NVCC_HOST_DEVICE_CHECK_DISABLE
6994
template <class... Tuples, std::size_t... Ints1, std::size_t... Ints2>
7095
KOKKOS_FORCEINLINE_FUNCTION constexpr tuple<cexa::tuple_element_t<
71-
Ints1,
72-
impl::remove_cvref_t<cexa::tuple_element_t<Ints2, tuple<Tuples...>>>>...>
73-
tuple_cat_impl(tuple<Tuples...>&& tuples, std::index_sequence<Ints1...>,
74-
std::index_sequence<Ints2...>) {
75-
return {get<Ints1>(std::move(get<Ints2>(tuples)))...};
96+
Ints1,
97+
std::remove_cvref_t<cexa::tuple_element_t<Ints2, tuple<Tuples...>>>>...>
98+
tuple_cat_impl(
99+
tuple<Tuples...>&& tuples,
100+
std::index_sequence<Ints1...>,
101+
std::index_sequence<Ints2...>)
102+
{
103+
return {get<Ints1>(std::move(get<Ints2>(tuples)))...};
76104
}
77-
} // namespace impl
105+
} // namespace impl
78106

79-
template <class... Tuples,
80-
class = std::enable_if_t<(impl::is_tuple_like<Tuples>::value && ...)>>
81-
KOKKOS_INLINE_FUNCTION constexpr auto tuple_cat(Tuples&&... tuples) {
82-
using cartesian_product_t = impl::cartesian_product<Tuples...>;
83-
return impl::tuple_cat_impl(cexa::forward_as_tuple(tuples...),
84-
typename cartesian_product_t::seq1{},
85-
typename cartesian_product_t::seq2{});
107+
template <class... Tuples>
108+
requires(impl::is_tuple_like<Tuples>::value && ...)
109+
KOKKOS_INLINE_FUNCTION constexpr auto tuple_cat(Tuples&&... tuples)
110+
{
111+
using cartesian_product_t = impl::cartesian_product<Tuples...>;
112+
return impl::tuple_cat_impl(
113+
cexa::forward_as_tuple(tuples...),
114+
typename cartesian_product_t::seq1 {},
115+
typename cartesian_product_t::seq2 {});
86116
}
87-
} // namespace cexa
117+
} // namespace cexa
118+
// NOLINTEND(readability-identifier-naming)

0 commit comments

Comments
 (0)