variant.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include "base/match_method.h"
  9. #include "base/assertion.h"
  10. #include <rpl/details/type_list.h>
  11. #include <variant>
  12. namespace v {
  13. namespace details {
  14. template <typename Type, typename ...Types>
  15. struct contains;
  16. template <typename Type, typename ...Types>
  17. inline constexpr bool contains_v = contains<Type, Types...>::value;
  18. template <typename Type, typename Head, typename ...Tail>
  19. struct contains<Type, Head, Tail...>
  20. : std::bool_constant<
  21. std::is_same_v<Head, Type> || contains_v<Type, Tail...>> {
  22. };
  23. template <typename Type>
  24. struct contains<Type> : std::bool_constant<false> {
  25. };
  26. } // namespace details
  27. using null_t = std::monostate;
  28. inline constexpr null_t null{};
  29. namespace type_list = rpl::details::type_list;
  30. template <typename ...Types>
  31. struct normalized_variant {
  32. using list = type_list::list<Types...>;
  33. using distinct = type_list::distinct_t<list>;
  34. using type = std::conditional_t<
  35. type_list::size_v<distinct> == 1,
  36. type_list::get_t<0, distinct>,
  37. type_list::extract_to_t<distinct, std::variant>>;
  38. };
  39. template <typename ...Types>
  40. using normalized_variant_t
  41. = typename normalized_variant<Types...>::type;
  42. template <typename TypeList, typename Variant, typename ...Methods>
  43. struct match_helper;
  44. template <
  45. typename Type,
  46. typename ...Types,
  47. typename Variant,
  48. typename ...Methods>
  49. struct match_helper<type_list::list<Type, Types...>, Variant, Methods...> {
  50. static decltype(auto) call(Variant &value, Methods &&...methods) {
  51. if (const auto v = std::get_if<Type>(&value)) {
  52. return base::match_method(
  53. *v,
  54. std::forward<Methods>(methods)...);
  55. }
  56. return match_helper<
  57. type_list::list<Types...>,
  58. Variant,
  59. Methods...>::call(
  60. value,
  61. std::forward<Methods>(methods)...);
  62. }
  63. };
  64. template <
  65. typename Type,
  66. typename Variant,
  67. typename ...Methods>
  68. struct match_helper<type_list::list<Type>, Variant, Methods...> {
  69. static decltype(auto) call(Variant &value, Methods &&...methods) {
  70. if (const auto v = std::get_if<Type>(&value)) {
  71. return base::match_method(
  72. *v,
  73. std::forward<Methods>(methods)...);
  74. }
  75. Unexpected("Valueless variant in base::match().");
  76. }
  77. };
  78. template <typename ...Types, typename ...Methods>
  79. inline decltype(auto) match(
  80. std::variant<Types...> &value,
  81. Methods &&...methods) {
  82. return match_helper<
  83. type_list::list<Types...>,
  84. std::variant<Types...>,
  85. Methods...>::call(value, std::forward<Methods>(methods)...);
  86. }
  87. template <typename ...Types, typename ...Methods>
  88. inline decltype(auto) match(
  89. const std::variant<Types...> &value,
  90. Methods &&...methods) {
  91. return match_helper<
  92. type_list::list<Types...>,
  93. const std::variant<Types...>,
  94. Methods...>::call(value, std::forward<Methods>(methods)...);
  95. }
  96. template <typename Type, typename ...Types>
  97. [[nodiscard]] inline bool is(const std::variant<Types...> &value) {
  98. return std::holds_alternative<Type>(value);
  99. }
  100. template <typename ...Types>
  101. [[nodiscard]] inline bool is_null(
  102. const std::variant<null_t, Types...> &value) {
  103. return is<null_t>(value);
  104. }
  105. // On macOS std::get is macOS 10.14+ because of the exception type.
  106. // So we use our own, implemented using std::get_if.
  107. template <typename Type, typename ...Types>
  108. [[nodiscard]] inline Type &get(std::variant<Types...> &value) {
  109. const auto result = std::get_if<Type>(&value);
  110. Ensures(result != nullptr);
  111. return *result;
  112. }
  113. template <typename Type, typename ...Types>
  114. [[nodiscard]] inline const Type &get(const std::variant<Types...> &value) {
  115. const auto result = std::get_if<Type>(&value);
  116. Ensures(result != nullptr);
  117. return *result;
  118. }
  119. } // namespace v
  120. template <
  121. typename ...Types,
  122. typename Type,
  123. typename = std::enable_if_t<v::details::contains_v<Type, Types...>>>
  124. inline bool operator==(const std::variant<Types...> &a, const Type &b) {
  125. return (a == std::variant<Types...>(b));
  126. }
  127. template <
  128. typename ...Types,
  129. typename Type,
  130. typename = std::enable_if_t<v::details::contains_v<Type, Types...>>>
  131. inline bool operator==(const Type &a, const std::variant<Types...> &b) {
  132. return (std::variant<Types...>(a) == b);
  133. }
  134. template <
  135. typename ...Types,
  136. typename Type,
  137. typename = std::enable_if_t<v::details::contains_v<Type, Types...>>>
  138. inline bool operator!=(const std::variant<Types...> &a, const Type &b) {
  139. return (a != std::variant<Types...>(b));
  140. }
  141. template <
  142. typename ...Types,
  143. typename Type,
  144. typename = std::enable_if_t<v::details::contains_v<Type, Types...>>>
  145. inline bool operator!=(const Type &a, const std::variant<Types...> &b) {
  146. return (std::variant<Types...>(a) != b);
  147. }