kblib 0.2.3
General utilities library for modern C++
variant.h
Go to the documentation of this file.
1/* *****************************************************************************
2 * kblib is a general utility library for C++14 and C++17, intended to provide
3 * performant high-level abstractions and more expressive ways to do simple
4 * things.
5 *
6 * Copyright (c) 2021 killerbee
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * ****************************************************************************/
21
32#ifndef KBLIB_VARIANT_H
33#define KBLIB_VARIANT_H
34
35#include "convert.h"
36#include "logic.h"
37#include "tdecl.h"
38
39#include <cstddef>
40#include <new>
41
42#if KBLIB_USE_CXX17
43# include <variant>
44#endif
45
46namespace KBLIB_NS {
47
48#if KBLIB_USE_CXX17
49
50template <typename T, typename = void>
51constexpr inline bool is_variant_like_v = false;
52template <typename T>
53constexpr inline bool is_variant_like_v<T, void_t<std::variant_size<T>>> = true;
54
55template <typename T>
56struct is_variant_like : std::bool_constant<is_variant_like_v<T>> {};
57
66template <typename To, typename... Ts>
67KBLIB_NODISCARD [[deprecated("Use new lexical_coerce instead.")]] auto coerce(
68 const std::variant<Ts...>& v) -> To {
69 return std::visit([](const auto& t) -> To { return lexical_cast<To>(t); },
70 v);
71}
72
79template <typename To, typename... Ts>
80KBLIB_NODISCARD auto static_coerce(const std::variant<Ts...>& v) -> To {
81 return std::visit([](const auto& t) -> To { return static_cast<To>(t); }, v);
82}
83
90template <typename To, typename... Ts>
91KBLIB_NODISCARD auto lexical_coerce(const std::variant<Ts...>& v) -> To {
92 return std::visit([](const auto& t) { return lexical_cast<To>(t); }, v);
93}
94
101template <typename... Ts>
102struct visitor : Ts... {
103 using Ts::operator()...;
104};
105template <typename... Ts>
106visitor(Ts...) -> visitor<Ts...>;
107
108namespace detail {
109
114 template <typename T>
115 struct tuple_type {
119 using type = std::tuple<>;
120 };
125 template <typename... Ts>
126 struct tuple_type<std::variant<Ts...>> {
130 using type = std::tuple<Ts...>;
131 };
135 template <typename T>
137
142 template <typename Variant, typename F, std::size_t... Is>
143 constexpr auto indexed_visitor_impl(std::index_sequence<Is...>) -> auto {
144 return std::array{+[](Variant&& variant, F&& f) {
145 return std::forward<F>(f)(
147 std::get<Is>(std::forward<Variant>(variant)));
148 }...};
149 }
150
151} // namespace detail
152
153inline namespace literals {
154
155 template <char... Cs>
156 KBLIB_NODISCARD constexpr auto operator""_vi() {
157 constexpr char arr[] = {Cs...};
158 return std::in_place_index_t<parse_integer<std::size_t>(arr)>{};
159 }
160
161} // namespace literals
162
175template <typename Variant, typename... Fs>
176constexpr auto visit_indexed(Variant&& variant, Fs&&... fs) -> decltype(auto) {
177 if (variant.valueless_by_exception()) {
178 throw std::bad_variant_access();
179 }
180 using visitor_t = decltype(visitor{std::forward<Fs>(fs)...});
181 return detail::indexed_visitor_impl<Variant, visitor_t>(
182 std::make_index_sequence<
183 std::variant_size_v<std::decay_t<Variant>>>())[variant.index()](
184 std::forward<Variant>(variant), visitor{std::forward<Fs>(fs)...});
185}
186
193template <typename To, typename From>
194KBLIB_NODISCARD constexpr auto variant_cast(From&& v) -> To {
195 static_assert(contains_types_v<detail::tuple_type_t<std::decay_t<To>>,
197 "To must include all types in From");
198
199 return visit_indexed(std::forward<From>(v), [](auto constant, auto&& x) {
200 return To(std::in_place_type<
201 std::variant_alternative_t<constant, std::decay_t<From>>>,
202 std::forward<decltype(x)>(x));
203 });
204
205 // return std::visit([](auto&& x) { return std::forward<decltype(x)>(x); },
206 // std::forward<From>(v));
207}
208
220template <typename V, typename F, typename... Fs>
221KBLIB_NODISCARD constexpr auto visit(V&& v, F&& f, Fs&&... fs)
222 -> decltype(auto) {
223 return std::visit(visitor{std::forward<F>(f), std::forward<Fs>(fs)...},
224 std::forward<V>(v));
225}
226
227namespace detail {
228
229 template <typename F, typename... Ts>
230 constexpr bool invocable_with_all_v
231 = (ignore_t<std::invoke_result_t<F, Ts>, std::true_type>::value and ...);
232
233 template <typename Callable, typename Variant>
234 constexpr bool v_invocable_with_all_v = false;
235
236 template <typename F, typename... Ts>
237 constexpr bool v_invocable_with_all_v<
238 F, std::variant<Ts...>> = invocable_with_all_v<F, Ts...>;
239
240 template <typename F, typename... Ts>
241 constexpr bool v_invocable_with_all_v<
242 F, const std::variant<Ts...>> = invocable_with_all_v<F, const Ts...>;
243
244 template <typename F, typename... Ts>
245 constexpr bool v_invocable_with_all_v<
246 F, std::variant<Ts...>&> = invocable_with_all_v<F, Ts&...>;
247
248 template <typename F, typename... Ts>
249 constexpr bool v_invocable_with_all_v<
250 F, const std::variant<Ts...>&> = invocable_with_all_v<F, const Ts&...>;
251
252 template <typename F, typename... Ts>
253 constexpr bool v_invocable_with_all_v<
254 F, std::variant<Ts...>&&> = invocable_with_all_v<F, Ts&&...>;
255
256 template <typename F, typename... Ts>
257 constexpr bool v_invocable_with_all_v<
258 F, const std::variant<Ts...>&&> = invocable_with_all_v<F, const Ts&&...>;
259
260 template <typename V, typename F, std::size_t I, std::size_t... Is>
261 [[gnu::always_inline]] constexpr decltype(auto) visit_impl(
262 V&& v, F&& f, std::index_sequence<I, Is...>) {
263 static_assert(I < std::variant_size_v<std::decay_t<V>>);
264 if (auto* p = std::get_if<I>(&v)) {
265 return std::forward<F>(f)(
266 static_cast<decltype(std::get<I>(std::forward<V>(v)))>(*p));
267 } else if constexpr (sizeof...(Is) > 0) {
268 return visit_impl(std::forward<V>(v), std::forward<F>(f),
269 std::index_sequence<Is...>{});
270 } else {
271 throw std::bad_variant_access();
272 }
273 }
274
275 template <typename V, typename F, std::size_t I, std::size_t... Is>
276 [[gnu::always_inline]] constexpr void visit_nop_impl(
277 V&& v, F&& f, std::index_sequence<I, Is...>) {
278 static_assert(I < std::variant_size_v<std::decay_t<V>>);
279 if (auto* p = std::get_if<I>(&v)) {
280 std::forward<F>(f)(
281 static_cast<decltype(std::get<I>(std::forward<V>(v)))>(*p));
282 return;
283 } else if constexpr (sizeof...(Is) > 0) {
284 return visit_nop_impl(std::forward<V>(v), std::forward<F>(f),
285 std::index_sequence<Is...>{});
286 } else {
287 // valueless_by_exception case
288 return;
289 }
290 }
291
292} // namespace detail
293
294template <typename V, typename F, typename... Fs>
295[[gnu::always_inline]] constexpr auto visit2(V&& v, F&& f, Fs&&... fs)
296 -> decltype(auto) {
297 auto visitor_obj = visitor{std::forward<F>(f), std::forward<Fs>(fs)...};
298 static_assert(detail::v_invocable_with_all_v<decltype(visitor_obj), V&&>,
299 "Some variant types not accepted by any visitors.");
300 return detail::visit_impl(
301 std::forward<V>(v), std::move(visitor_obj),
302 std::make_index_sequence<std::variant_size_v<std::decay_t<V>>>{});
303}
304
305template <typename V, typename F, typename... Fs>
306[[gnu::always_inline]] constexpr auto visit2_nop(V&& v, F&& f, Fs&&... fs)
307 -> void {
308 auto visitor_obj = visitor{std::forward<F>(f), std::forward<Fs>(fs)...};
309 static_assert(detail::v_invocable_with_all_v<decltype(visitor_obj), V&&>,
310 "Some variant types not accepted by any visitors.");
312 std::forward<V>(v), visitor_obj,
313 std::make_index_sequence<std::variant_size_v<std::decay_t<V>>>{});
314}
315
331template <typename V>
332KBLIB_NODISCARD constexpr auto visit(V& v) -> auto {
333 return [&v](auto... fs) -> decltype(auto) {
334 return kblib::visit(v, std::forward<decltype(fs)>(fs)...);
335 };
336}
337
338#endif // KBLIB_USE_CXX17
339
340} // namespace KBLIB_NS
341
342#endif // KBLIB_VARIANT_H
Provides facilities to convert between various kinds of representations.
Provides basic compile-time logic operations.
constexpr bool invocable_with_all_v
Definition: variant.h:231
constexpr auto indexed_visitor_impl(std::index_sequence< Is... >) -> auto
Generates an array of function pointers which will unwrap the variant and pass the index to the funct...
Definition: variant.h:143
typename tuple_type< T >::type tuple_type_t
Definition: variant.h:136
constexpr decltype(auto) visit_impl(V &&v, F &&f, std::index_sequence< I, Is... >)
Definition: variant.h:261
constexpr void visit_nop_impl(V &&v, F &&f, std::index_sequence< I, Is... >)
Definition: variant.h:276
constexpr bool v_invocable_with_all_v
Definition: variant.h:234
auto static_coerce(const std::variant< Ts... > &v) -> To
static_casts the value of v (no matter its type) to type To.
Definition: variant.h:80
typename ignore< U, T >::type ignore_t
An alias for ignore<U, T>::type.
Definition: traits.h:169
auto coerce(const std::variant< Ts... > &v) -> To
Lexically converts the value of v (no matter its type) to type To.
Definition: variant.h:67
constexpr bool is_variant_like_v
Definition: variant.h:51
constexpr auto visit_indexed(Variant &&variant, Fs &&... fs) -> decltype(auto)
Visit a variant, but pass the index (as an integral_constant) to the visitor. This allows for a visit...
Definition: variant.h:176
constexpr auto visit2_nop(V &&v, F &&f, Fs &&... fs) -> void
Definition: variant.h:306
visitor(Ts...) -> visitor< Ts... >
constexpr auto variant_cast(From &&v) -> To
Promotes an input variant to a super-variant. That is, one which provides at least the same set of ty...
Definition: variant.h:194
std::integral_constant< bool, v > bool_constant
Definition: fakestd.h:64
constexpr auto visit(V &v) -> auto
Two-step visiting interface. Takes a variant, and returns an object which can be called with any numb...
Definition: variant.h:332
constexpr auto visit2(V &&v, F &&f, Fs &&... fs) -> decltype(auto)
Definition: variant.h:295
auto lexical_coerce(const std::variant< Ts... > &v) -> To
Lexically converts the value of v (no matter its type) to type To.
Definition: variant.h:91
constexpr auto visit(V &&v, F &&f, Fs &&... fs) -> decltype(auto)
Wraps std::visit to provide an interface taking one variant and any number of functors providing an o...
Definition: variant.h:221
Definition: bits.h:721
std::tuple< Ts... > type
A tuple of the same types as T is a variant of.
Definition: variant.h:130
Given a std::variant T, provides the member type which is a tuple of the same types.
Definition: variant.h:115
std::tuple<> type
Non-variant inputs produce the empty tuple.
Definition: variant.h:119
Helper class for std::visiting a std::variant.
Definition: variant.h:102
Provides macros and basic templates used by the rest of kblib.
#define KBLIB_NS
Definition: tdecl.h:113
#define KBLIB_NODISCARD
This internal macro is used to provide a fallback for [[nodiscard]] in C++14.
Definition: tdecl.h:129