Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * \file
- * \brief C++-ish sort-of switch statement reimplementation.
- * \author Chris Smeele
- *
- * Copyright (c) 2017, Chris Smeele
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #pragma once
- #include <tuple>
- #include <type_traits>
- // Usage: match(value, [test_value, code]...)
- // test_value can be one of:
- // - a bool, runs the code if true
- // - an x,y pair, runs code if x <= value <= y
- // - something that can be ==-compared to `value`, runs code if they are equal
- // - a callable that receives `value`, runs code if result is true
- //
- // The result of `code` is used to determine what to do next:
- // - If the code returns MatchAction::satisfy, the match is exited
- // - If the code returns MatchAction::next, the match continues with the next check
- // - If the code returns MatchAction::fallthrough, the match immediately continues
- // by executing the next code block, ignoring its test_value.
- // - If the code returns a boolean, true means satisfy, false means next.
- // - If the code does _not_ return any value, MatchAction::satisfy is implied,
- // so it exits the match.
- //
- // Instead of a callable, you can put one of the MatchAction values into the place of `code`.
- //
- // The following match:
- //
- // match(rand() % 10,
- // [](int n) { return n&1; }, [] { std::cout << "odd number\n"; return match_next; }, // catches any odd number, but continues checking other cases
- // 0, [] { std::cout << "0\n"; }, // match_satisfy is implied
- // 3, [] { std::cout << "3\n"; return match_next; }, // could also be fallthrough
- // std::pair(1,4), [] { std::cout << "1-4\n"; return match_fallthrough; },
- // 8, [] { std::cout << "1-4 or 8\n"; }, // match_satisfy is implied
- // true, [] { std::cout << "number was not interesting enough\n"; }); // fires for 5,6,7,9
- //
- // Has the following C switch equivalent:
- //
- // int n = rand() % 10;
- // bool uninteresting = false;
- // if (n & 1) { std::cout << "odd number\n"; uninteresting = true; }
- // switch(n) {
- // case 0: std::cout << "0\n"; break;
- // case 3: std::cout << "3\n"; uninteresting = false;
- // case 1:
- // case 2:
- // case 4: std::cout << "1-4\n";
- // case 8: std::cout << "1-4 or 8\n"; uninteresting = false; break;
- // default: uninteresting = true;
- // }
- // if (uninteresting)
- // std::cout << "number was not interesting enough\n";
- //
- //
- // The equivalent of the switch `default` case is a `true` test value at the end of the match.
- // If for a given value none of the cases returns MatchAction::satisfy, this `true` case will be executed.
- // Simsalabim.
- template<class...> using void_t = void;
- // Is T1 == T2 valid?
- template<typename T1, typename T2, typename=void>
- struct is_equality_comparable : std::false_type { };
- template<typename T1, typename T2>
- struct is_equality_comparable<T1,T2,void_t<decltype(std::declval<T1>() == std::declval<T2>())>> : std::true_type { };
- // Is T1 <= T2 valid?
- template<typename T1, typename T2, typename=void>
- struct is_lte_comparable : std::false_type { };
- template<typename T1, typename T2>
- struct is_lte_comparable<T1,T2,void_t<decltype(std::declval<T1>() <= std::declval<T2>())>> : std::true_type { };
- // Are P::first <= T and T <= P::second valid?
- template<typename T, typename P, typename=void>
- struct is_pair_of_lte2_comparable : std::false_type { };
- template<typename T, typename P>
- struct is_pair_of_lte2_comparable<T,P,void_t<decltype(std::declval<decltype(P::first)>() <= std::declval<T>()),
- decltype(std::declval<T>() <= std::declval<decltype(P::second)>())>> : std::true_type { };
- enum class MatchAction {
- satisfy = 0,
- next,
- fallthrough,
- };
- constexpr auto match_satisfy = MatchAction::satisfy;
- constexpr auto match_next = MatchAction::next;
- constexpr auto match_fallthrough = MatchAction::fallthrough;
- template<typename T>
- constexpr void match_impl(const T &x, MatchAction) {
- return (void)x;
- }
- template<typename T, typename V1, typename F1, typename... R>
- constexpr void match_impl(const T &x, MatchAction ma, const V1 &v1, F1 f1, R... rest) {
- // Checking on bools doesn't really make sense.
- // Reserve bools for an always-match case.
- static_assert(!std::is_same<T,bool>::value, "For boolean matches, use if/else etc. instead.");
- MatchAction action = MatchAction::next;
- // The case body.
- auto ff = [&] {
- if constexpr (std::is_same<MatchAction,F1>::value)
- return [&] { return f1; };
- else if constexpr (std::is_same<MatchAction,decltype(f1())>::value)
- return f1;
- else if constexpr (std::is_same<bool,decltype(f1())>::value)
- return [&] { return f1() ? match_satisfy : match_next; };
- else
- return [&] { f1(); return match_satisfy; };
- }();
- if (ma == MatchAction::fallthrough) {
- // The previous case did a fallthrough, skip the check.
- action = ff();
- } else if (ma == MatchAction::next) {
- // The previous case did not match or was not satisfactory.
- if constexpr (std::is_same<V1,bool>::value) {
- if (v1)
- action = ff();
- } else if constexpr (is_pair_of_lte2_comparable<T,V1>::value) {
- if (v1.first <= x && x <= v1.second)
- action = ff();
- } else if constexpr (is_equality_comparable<T,V1>::value) {
- if (x == v1)
- action = ff();
- } else {
- if (v1(x))
- action = ff();
- }
- }
- if (action == MatchAction::satisfy)
- return;
- else
- match_impl(x, action, rest...);
- }
- template<typename T, typename... R>
- constexpr void match(const T &x, R... rest) {
- match_impl(x, match_next, rest...);
- }
Add Comment
Please, Sign In to add comment