模式匹配中的失败

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 link

您可以检查 return 您的“案例”类型:

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)...);
}

Demo

请注意,我将您的 "push_front" 更改为 ""=23=]" 以获得正确的顺序