Guest User

Untitled

a guest
Nov 14th, 2017
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.76 KB | None | 0 0
  1. /**
  2. * \file
  3. * \brief C++-ish sort-of switch statement reimplementation.
  4. * \author Chris Smeele
  5. *
  6. * Copyright (c) 2017, Chris Smeele
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #pragma once
  27.  
  28. #include <tuple>
  29. #include <type_traits>
  30.  
  31. // Usage: match(value, [test_value, code]...)
  32. // test_value can be one of:
  33. // - a bool, runs the code if true
  34. // - an x,y pair, runs code if x <= value <= y
  35. // - something that can be ==-compared to `value`, runs code if they are equal
  36. // - a callable that receives `value`, runs code if result is true
  37. //
  38. // The result of `code` is used to determine what to do next:
  39. // - If the code returns MatchAction::satisfy, the match is exited
  40. // - If the code returns MatchAction::next, the match continues with the next check
  41. // - If the code returns MatchAction::fallthrough, the match immediately continues
  42. // by executing the next code block, ignoring its test_value.
  43. // - If the code returns a boolean, true means satisfy, false means next.
  44. // - If the code does _not_ return any value, MatchAction::satisfy is implied,
  45. // so it exits the match.
  46. //
  47. // Instead of a callable, you can put one of the MatchAction values into the place of `code`.
  48. //
  49. // The following match:
  50. //
  51. // match(rand() % 10,
  52. // [](int n) { return n&1; }, [] { std::cout << "odd number\n"; return match_next; }, // catches any odd number, but continues checking other cases
  53. // 0, [] { std::cout << "0\n"; }, // match_satisfy is implied
  54. // 3, [] { std::cout << "3\n"; return match_next; }, // could also be fallthrough
  55. // std::pair(1,4), [] { std::cout << "1-4\n"; return match_fallthrough; },
  56. // 8, [] { std::cout << "1-4 or 8\n"; }, // match_satisfy is implied
  57. // true, [] { std::cout << "number was not interesting enough\n"; }); // fires for 5,6,7,9
  58. //
  59. // Has the following C switch equivalent:
  60. //
  61. // int n = rand() % 10;
  62. // bool uninteresting = false;
  63. // if (n & 1) { std::cout << "odd number\n"; uninteresting = true; }
  64. // switch(n) {
  65. // case 0: std::cout << "0\n"; break;
  66. // case 3: std::cout << "3\n"; uninteresting = false;
  67. // case 1:
  68. // case 2:
  69. // case 4: std::cout << "1-4\n";
  70. // case 8: std::cout << "1-4 or 8\n"; uninteresting = false; break;
  71. // default: uninteresting = true;
  72. // }
  73. // if (uninteresting)
  74. // std::cout << "number was not interesting enough\n";
  75. //
  76. //
  77. // The equivalent of the switch `default` case is a `true` test value at the end of the match.
  78. // If for a given value none of the cases returns MatchAction::satisfy, this `true` case will be executed.
  79.  
  80. // Simsalabim.
  81. template<class...> using void_t = void;
  82.  
  83. // Is T1 == T2 valid?
  84. template<typename T1, typename T2, typename=void>
  85. struct is_equality_comparable : std::false_type { };
  86. template<typename T1, typename T2>
  87. struct is_equality_comparable<T1,T2,void_t<decltype(std::declval<T1>() == std::declval<T2>())>> : std::true_type { };
  88.  
  89. // Is T1 <= T2 valid?
  90. template<typename T1, typename T2, typename=void>
  91. struct is_lte_comparable : std::false_type { };
  92. template<typename T1, typename T2>
  93. struct is_lte_comparable<T1,T2,void_t<decltype(std::declval<T1>() <= std::declval<T2>())>> : std::true_type { };
  94.  
  95. // Are P::first <= T and T <= P::second valid?
  96. template<typename T, typename P, typename=void>
  97. struct is_pair_of_lte2_comparable : std::false_type { };
  98. template<typename T, typename P>
  99. struct is_pair_of_lte2_comparable<T,P,void_t<decltype(std::declval<decltype(P::first)>() <= std::declval<T>()),
  100. decltype(std::declval<T>() <= std::declval<decltype(P::second)>())>> : std::true_type { };
  101.  
  102. enum class MatchAction {
  103. satisfy = 0,
  104. next,
  105. fallthrough,
  106. };
  107. constexpr auto match_satisfy = MatchAction::satisfy;
  108. constexpr auto match_next = MatchAction::next;
  109. constexpr auto match_fallthrough = MatchAction::fallthrough;
  110.  
  111. template<typename T>
  112. constexpr void match_impl(const T &x, MatchAction) {
  113. return (void)x;
  114. }
  115.  
  116. template<typename T, typename V1, typename F1, typename... R>
  117. constexpr void match_impl(const T &x, MatchAction ma, const V1 &v1, F1 f1, R... rest) {
  118.  
  119. // Checking on bools doesn't really make sense.
  120. // Reserve bools for an always-match case.
  121. static_assert(!std::is_same<T,bool>::value, "For boolean matches, use if/else etc. instead.");
  122.  
  123. MatchAction action = MatchAction::next;
  124.  
  125. // The case body.
  126. auto ff = [&] {
  127. if constexpr (std::is_same<MatchAction,F1>::value)
  128. return [&] { return f1; };
  129. else if constexpr (std::is_same<MatchAction,decltype(f1())>::value)
  130. return f1;
  131. else if constexpr (std::is_same<bool,decltype(f1())>::value)
  132. return [&] { return f1() ? match_satisfy : match_next; };
  133. else
  134. return [&] { f1(); return match_satisfy; };
  135. }();
  136.  
  137. if (ma == MatchAction::fallthrough) {
  138. // The previous case did a fallthrough, skip the check.
  139. action = ff();
  140. } else if (ma == MatchAction::next) {
  141. // The previous case did not match or was not satisfactory.
  142. if constexpr (std::is_same<V1,bool>::value) {
  143. if (v1)
  144. action = ff();
  145. } else if constexpr (is_pair_of_lte2_comparable<T,V1>::value) {
  146. if (v1.first <= x && x <= v1.second)
  147. action = ff();
  148. } else if constexpr (is_equality_comparable<T,V1>::value) {
  149. if (x == v1)
  150. action = ff();
  151. } else {
  152. if (v1(x))
  153. action = ff();
  154. }
  155. }
  156.  
  157. if (action == MatchAction::satisfy)
  158. return;
  159. else
  160. match_impl(x, action, rest...);
  161. }
  162.  
  163. template<typename T, typename... R>
  164. constexpr void match(const T &x, R... rest) {
  165. match_impl(x, match_next, rest...);
  166. }
Add Comment
Please, Sign In to add comment