簡體   English   中英

模式匹配失敗

[英]Fallthrough in pattern matching

最近需要編寫一個匹配模式來捕獲錯誤,它實際上可以應對。 但與相同的switch相比,它唯一缺乏的是失敗的機會。 我實現此功能的所有嘗試均未成功。 最初,我想過這樣的事情:

matcher(errcode)
        .match(SQLITE_MISUSE    , [] { return test::Fallthrough{}; })
        .match(SQLITE_CONSTRAINT, [] { return test::Fallthrough{}; })
        .match(SQLITE_ERROR     , [] { throw std::runtime_error("XXX"); })
        .match(SQLITE_BUSY      , [] { })
        ;

但我未能實現它。 所以,在下面,我附上了我的“不成功”版本的代碼草稿。 我想以某種方式實施它,我很樂意提供任何建議。

#include <iostream>
#include <tuple>
#include <optional>
#include <type_traits>
#include <utility>
#include <optional>

namespace test {

namespace detail {

struct Void final {};

template <typename T> T operator,(T const& v, Void) noexcept {
    return v;
}

} // namespace detail

template <typename T, typename Fn> struct Match_pack {
    T  v;
    Fn fn;
};

struct Fallthrough {};

template <typename T> bool invoke(T) {
    return false;
}

template <typename T, typename Pack, typename... Tail>
bool invoke(T v, Pack&& pack, Tail&&... tail) {
    if (pack.v == v) {
        auto rv = (pack.fn(), detail::Void{});
        // ...
        return true;
    }
    return invoke(v, std::forward<Tail>(tail)...);
}

template <typename...> struct Match_args;

template <typename... Args> struct Matcher {

    Matcher(Args... args) : vs_{args...}
    { }

    template <typename E, typename Fn> auto match(E e, Fn fn)
        -> Match_args<Matcher<Args...>, Match_pack<E, Fn>>
    {
        return {std::move(*this), {e, fn}};
    }

protected:
    std::tuple<Args...> vs_;
};

template <typename... Args, typename... Tail>
struct Match_args<Matcher<Args...>, Tail...> : Matcher<Args...>, Tail... {

    Match_args(Matcher<Args...>&& args, Tail&&... tail)
        : Matcher<Args...>{std::move(args)}
        , Tail(std::move(tail))...
    { }

    template <typename E, typename Fn> auto match(E e, Fn fn) 
        -> Match_args<Matcher<Args...>, Match_pack<E, Fn>, Tail...>
    {
        return {std::move(*this), {e, fn}, std::forward<Tail>(*this)...};
    }

    operator bool() {
        return std::apply([&](auto... vs) {
            return (invoke<decltype(vs), Tail...>(vs, std::forward<Tail>(*this)...) && ...);
        }, this->vs_);
    }
};

} // namespace test


enum {
    SQLITE_ERROR,
    SQLITE_MISUSE,
    SQLITE_CONSTRAINT,
    SQLITE_DONE,
    SQLITE_ROW,
    SQLITE_BUSY
};

auto step() {
    return SQLITE_CONSTRAINT;   
}

template <typename... Args>
auto matcher(Args... args) {
    return test::Matcher{args...}; 
}

std::optional<int> foo() {
    auto errcode = step();
    if (SQLITE_DONE == errcode) return std::nullopt;
    if (SQLITE_ROW  == errcode) return 1;

    bool f = matcher(errcode)
        .match(SQLITE_MISUSE    , [] { std::cout << "SQLITE_MISUSE\n";     return test::Fallthrough{}; })
        .match(SQLITE_CONSTRAINT, [] { std::cout << "SQLITE_CONSTRAINT\n"; return test::Fallthrough{}; })
        .match(SQLITE_ERROR     , [] { std::cout << "SQLITE_ERROR\n"; throw std::runtime_error("XXX"); })
        .match(SQLITE_BUSY      , [] { std::cout << "SQLITE_BUSY\n" ; })
        ;

    std::cout << f;
    return 2;
}

int main() {
    try {
        auto x = foo();
    } catch(...) {
    }
}

Godbolt 鏈接

您可以檢查“案例”的返回類型:

template <typename T, typename Pack, typename... Tail>
bool invoke(T v, Pack&& pack, Tail&&... tail) {
    if (pack.v == v) {
        auto rv = (pack.fn(), detail::Void{});
        if constexpr (std::is_same_v<Fallthrough, decltype(rv)>) {
            static_cast<void>( // To avoid warning for clang
                ((tail.fn(), std::is_same_v<Fallthrough, decltype(tail.fn())>) && ...)
            );
        }
        return true;
    }
    return invoke(v, std::forward<Tail>(tail)...);
}

演示

請注意,我將您的“push_front”更改為“push_back”以獲得正確的順序

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM