模式匹配中的失败
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(...) {
}
}
您可以检查 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)...);
}
请注意,我将您的 "push_front" 更改为 ""=23=]" 以获得正确的顺序
最近需要写一个捕获错误的匹配模式,它实际上解决了这个问题。但与相同的 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(...) {
}
}
您可以检查 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)...);
}
请注意,我将您的 "push_front" 更改为 ""=23=]" 以获得正确的顺序