Skip to content

Commit 2677ef6

Browse files
authored
Merge pull request #120 from elbeno/apply-sequence
🆕 Add `apply_sequence`
2 parents 54abfb1 + cb82b3d commit 2677ef6

File tree

3 files changed

+122
-30
lines changed

3 files changed

+122
-30
lines changed

docs/type_traits.adoc

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,38 @@ auto f(T) {
3636
};
3737
----
3838

39+
=== `apply_sequence`
40+
41+
A xref:type_traits.adoc#_type_list_and_value_list[`type_list` or a `value_list`]
42+
can be unpacked and passed as individual template arguments with
43+
`apply_sequence`. A function object whose call operator is a variadic function
44+
template with no runtime arguments is called with the pack of arguments.
45+
46+
[source,cpp]
47+
----
48+
using L1 = stdx::type_list<std::integral_constant<int, 1>,
49+
std::integral_constant<int, 2>>;
50+
int x = stdx::apply_sequence<L1>([&] <typename... Ts> () { return (0 + ... + Ts::value); });
51+
// x is 3
52+
53+
using L2 = stdx::value_list<1, 2>;
54+
int y = stdx::apply_sequence<L1>([&] <auto... Vs> () { return (0 + ... + Vs); });
55+
// y is 3
56+
----
57+
58+
`apply_sequence` can also be used with a
59+
https://en.cppreference.com/w/cpp/utility/integer_sequence[`std::integer_sequence`]:
60+
61+
[source,cpp]
62+
----
63+
using L3 = stdx::make_index_sequence<3>;
64+
auto y = stdx::apply_sequence<L3>([&] <auto... Vs> () { y += V; });
65+
// y is 3
66+
----
67+
68+
NOTE: If the function iterates the pack by folding over `operator,` then
69+
xref:type_traits.adoc#_template_for_each[`template_for_each`] is probably what you want.
70+
3971
=== `is_function_object_v`
4072

4173
`is_function_object_v` is a variable template that detects whether a type is a
@@ -52,6 +84,17 @@ stdx::is_function_object_v<decltype(lam)>; // true
5284
stdx::is_function_object_v<decltype(gen_lam)>; // true
5385
----
5486

87+
=== `is_same_unqualified_v`
88+
89+
`is_same_unqualified_v` is a variable template that detects whether a two types
90+
are the same are removing top-level cv-qualifications and references, if any.
91+
92+
[source,cpp]
93+
----
94+
stdx::is_same_unqualified_v<int, int const&>; // true
95+
stdx::is_same_unqualified_v<int, void>; // false
96+
----
97+
5598
=== `is_specialization_of_v`
5699

57100
`is_specialization_of_v` is a variable template that detects whether a type is a
@@ -112,31 +155,12 @@ NOTE: Detecting structurality of a type is not yet possible in the general case,
112155
so there are certain structural types for which this trait will be `false`. In
113156
practice those types should be rare, and there should be no false positives.
114157

115-
=== `type_or_t`
116-
117-
`type_or_t` is an alias template that selects a type based on whether or not it
118-
passes a predicate. If not, a default is returned.
119-
120-
[source,cpp]
121-
----
122-
using A = int *;
123-
using T = stdx::type_or_t<std::is_pointer, A>; // A
124-
125-
using B = int;
126-
using X = stdx::type_or_t<std::is_pointer, B>; // void (implicit default)
127-
using Y = stdx::type_or_t<std::is_pointer, B, float>; // float (explicit default)
128-
----
129-
130-
=== `type_list` and `value_list`
131-
132-
`type_list` is an empty `struct` templated over any number of types.
133-
`value_list` is an empty `struct` templated over any number of NTTPs.
134-
135158
=== `template_for_each`
136159

137-
A `type_list` or a `value_list` can be iterated with `template_for_each`. A
138-
function object whose call operator is a unary function template with no runtime
139-
arguments is passed to each of these functions.
160+
A xref:type_traits.adoc#_type_list_and_value_list[`type_list` or a `value_list`]
161+
can be iterated with `template_for_each`. A function object whose call operator
162+
is a unary function template with no runtime arguments is called with each
163+
element of the list.
140164

141165
[source,cpp]
142166
----
@@ -158,21 +182,31 @@ https://en.cppreference.com/w/cpp/utility/integer_sequence[`std::integer_sequenc
158182
[source,cpp]
159183
----
160184
using L3 = stdx::make_index_sequence<3>;
161-
int y{};
185+
std::size_t y{};
162186
stdx::template_for_each<L3>([&] <auto V> () { y += V; });
163187
// y is now 3
164188
----
165189

166190
NOTE: A primary use case of `template_for_each` is to be able to use a list of
167191
tag types without those types having to be complete.
168192

169-
=== `is_same_unqualified_v`
193+
=== `type_or_t`
170194

171-
`is_same_unqualified_v` is a variable template that detects whether a two types
172-
are the same are removing top-level cv-qualifications and references, if any.
195+
`type_or_t` is an alias template that selects a type based on whether or not it
196+
passes a predicate. If not, a default is returned.
173197

174198
[source,cpp]
175199
----
176-
stdx::is_same_unqualified_v<int, int const&>; // true
177-
stdx::is_same_unqualified_v<int, void>; // false
200+
using A = int *;
201+
using T = stdx::type_or_t<std::is_pointer, A>; // A
202+
203+
using B = int;
204+
using X = stdx::type_or_t<std::is_pointer, B>; // void (implicit default)
205+
using Y = stdx::type_or_t<std::is_pointer, B, float>; // float (explicit default)
178206
----
207+
208+
=== `type_list` and `value_list`
209+
210+
`type_list` is an empty `struct` templated over any number of types.
211+
`value_list` is an empty `struct` templated over any number of NTTPs.
212+

include/stdx/type_traits.hpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ constexpr bool is_scoped_enum_v =
118118
template <typename...> struct type_list {};
119119
template <auto...> struct value_list {};
120120

121+
namespace detail {
121122
template <typename L> struct for_each_t {
122123
static_assert(always_false_v<L>,
123124
"template_for_each must be called with a type list, "
@@ -142,8 +143,40 @@ struct for_each_t<L<T, Vs...>> {
142143
(f.template operator()<Vs>(), ...);
143144
}
144145
};
146+
} // namespace detail
147+
148+
template <typename L>
149+
constexpr static auto template_for_each = detail::for_each_t<L>{};
150+
151+
namespace detail {
152+
template <typename L> struct apply_sequence_t {
153+
static_assert(always_false_v<L>,
154+
"apply_sequence must be called with a type list, "
155+
"value_list, or std::integer_sequence");
156+
};
157+
158+
template <template <typename...> typename L, typename... Ts>
159+
struct apply_sequence_t<L<Ts...>> {
160+
template <typename F> constexpr auto operator()(F &&f) const {
161+
return f.template operator()<Ts...>();
162+
}
163+
};
164+
template <template <auto...> typename L, auto... Vs>
165+
struct apply_sequence_t<L<Vs...>> {
166+
template <typename F> constexpr auto operator()(F &&f) const {
167+
return f.template operator()<Vs...>();
168+
}
169+
};
170+
template <template <typename, auto...> typename L, typename T, T... Vs>
171+
struct apply_sequence_t<L<T, Vs...>> {
172+
template <typename F> constexpr auto operator()(F &&f) const {
173+
return f.template operator()<Vs...>();
174+
}
175+
};
176+
} // namespace detail
145177

146-
template <typename L> constexpr static auto template_for_each = for_each_t<L>{};
178+
template <typename L>
179+
constexpr static auto apply_sequence = detail::apply_sequence_t<L>{};
147180

148181
template <typename T, typename U>
149182
constexpr bool is_same_unqualified_v =

test/type_traits.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ struct add_value {
100100
}
101101
template <auto T> constexpr auto operator()() const -> void { value += T; }
102102
};
103+
104+
struct add_values {
105+
template <typename... Ts> constexpr auto operator()() const {
106+
return (0 + ... + Ts::value);
107+
}
108+
template <auto... Ts> constexpr auto operator()() const {
109+
return (0 + ... + Ts);
110+
}
111+
};
103112
} // namespace
104113

105114
TEST_CASE("template_for_each with type list", "[type_traits]") {
@@ -138,6 +147,22 @@ TEST_CASE("template_for_each with index sequence", "[type_traits]") {
138147
CHECK(value == 3);
139148
}
140149

150+
TEST_CASE("apply_sequence with type list", "[type_traits]") {
151+
using L = stdx::type_list<std::integral_constant<int, 1>,
152+
std::integral_constant<int, 2>>;
153+
CHECK(stdx::apply_sequence<L>(add_values{}) == 3);
154+
}
155+
156+
TEST_CASE("apply_sequence with value list", "[type_traits]") {
157+
using L = stdx::value_list<1, 2>;
158+
CHECK(stdx::apply_sequence<L>(add_values{}) == 3);
159+
}
160+
161+
TEST_CASE("apply_sequence with index sequence", "[type_traits]") {
162+
using L = std::make_index_sequence<3>;
163+
CHECK(stdx::apply_sequence<L>(add_values{}) == 3);
164+
}
165+
141166
TEST_CASE("is_same_unqualified", "[type_traits]") {
142167
static_assert(stdx::is_same_unqualified_v<int, int>);
143168
static_assert(not stdx::is_same_unqualified_v<int, void>);

0 commit comments

Comments
 (0)