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 
46 namespace kblib {
47 
48 #if KBLIB_USE_CXX17
49 
50 template <typename T, typename = void>
51 constexpr inline bool is_variant_like_v = false;
52 template <typename T>
53 constexpr inline bool is_variant_like_v<T, void_t<std::variant_size<T>>> = true;
54 
55 template <typename T>
56 struct is_variant_like : std::bool_constant<is_variant_like_v<T>> {};
57 
66 template <typename To, typename... Ts>
67 KBLIB_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 
79 template <typename To, typename... Ts>
80 KBLIB_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 
90 template <typename To, typename... Ts>
91 KBLIB_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 
101 template <typename... Ts>
102 struct visitor : Ts... {
103  using Ts::operator()...;
104 };
105 template <typename... Ts>
106 visitor(Ts...) -> visitor<Ts...>;
107 
108 namespace 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 
153 inline 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 
175 template <typename Variant, typename... Fs>
176 constexpr 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 
193 template <typename To, typename From>
194 KBLIB_NODISCARD constexpr auto variant_cast(From&& v) -> To {
195  static_assert(contains_types_v<detail::tuple_type_t<std::decay_t<To>>,
196  detail::tuple_type_t<std::decay_t<From>>>,
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 
220 template <typename V, typename F, typename... Fs>
221 KBLIB_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 
227 namespace 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 
294 template <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 
305 template <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.");
311  return detail::visit_nop_impl(
312  std::forward<V>(v), visitor_obj,
313  std::make_index_sequence<std::variant_size_v<std::decay_t<V>>>{});
314 }
315 
331 template <typename V>
332 KBLIB_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
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
The main namespace in which all entities from kblib are defined.
Definition: algorithm.h:44
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
KBLIB_CONSTANT_V contains_types_v
Definition: traits.h:84
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:714
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_NODISCARD
This internal macro is used to provide a fallback for [[nodiscard]] in C++14.
Definition: tdecl.h:81