[英]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(...) {
}
}
您可以檢查“案例”的返回類型:
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.