SHARE
TWEET

Untitled

a guest Nov 14th, 2017 56 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
Top