/* * Copyright (c) 2021-2022 Bowen Fu * Distributed Under The Apache-2.0 License */ #ifndef MATCHIT_H #define MATCHIT_H #ifndef MATCHIT_CORE_H #define MATCHIT_CORE_H #include #include #include namespace matchit { // NOLINTBEGIN(*-identifier-naming) namespace impl { template class ValueType { public: using ValueT = Value; }; template class ValueType { public: using ValueT = Value&&; }; template constexpr auto matchPatterns(Value&& value, const Patterns&... patterns); template class MatchHelper { private: using ValueT = typename ValueType::ValueT; ValueT mValue; using ValueRefT = ValueT&&; public: template constexpr explicit MatchHelper(V&& value) : mValue { std::forward(value) } {} template constexpr auto operator()(const PatternPair&... patterns) { return matchPatterns(std::forward(mValue), patterns...); } }; template constexpr auto match(Value&& value) { return MatchHelper { std::forward(value) }; } template constexpr auto match(First&& first, Values&&... values) { auto result = std::forward_as_tuple(std::forward(first), std::forward(values)...); return MatchHelper { std::forward(result) }; } } // namespace impl // export symbols using impl::match; } // namespace matchit #endif // MATCHIT_CORE_H #ifndef MATCHIT_EXPRESSION_H #define MATCHIT_EXPRESSION_H #include namespace matchit { namespace impl { template class Nullary : public T { public: using T::operator(); }; template constexpr auto nullary(const T& t) { return Nullary { t }; } template class Id; template constexpr auto expr(Id& id) { return nullary([&] { return *id; }); } template constexpr auto expr(const T& v) { return nullary([&] { return v; }); } template constexpr auto toNullary(T&& v) { if constexpr (std::is_invocable_v>) { return v; } else { return expr(v); } } // for constant template class EvalTraits { public: template constexpr static decltype(auto) evalImpl(const T& v, const Args&...) { return v; } }; template class EvalTraits> { public: constexpr static decltype(auto) evalImpl(const Nullary& e) { return e(); } }; // Only allowed in nullary template class EvalTraits> { public: constexpr static decltype(auto) evalImpl(const Id& id) { return *const_cast&>(id); } }; template class Meet; // Unary is an alias of Meet. template using Unary = Meet; template class EvalTraits> { public: template constexpr static decltype(auto) evalImpl(const Unary& e, const Arg& arg) { return e(arg); } }; class Wildcard; template <> class EvalTraits { public: template constexpr static decltype(auto) evalImpl(const Wildcard&, const Arg& arg) { return arg; } }; template constexpr decltype(auto) evaluate_(const T& t, const Args&... args) { return EvalTraits::evalImpl(t, args...); } template class IsNullaryOrId : public std::false_type {}; template class IsNullaryOrId> : public std::true_type {}; template class IsNullaryOrId> : public std::true_type {}; template constexpr auto isNullaryOrIdV = IsNullaryOrId>::value; #define UN_OP_FOR_NULLARY(op) \ template , bool> = true> \ constexpr auto operator op(T const& t) { \ return nullary([&] { return op evaluate_(t); }); \ } #define BIN_OP_FOR_NULLARY(op) \ template || isNullaryOrIdV, bool> = true> \ constexpr auto operator op(T const& t, U const& u) { \ return nullary([&] { return evaluate_(t) op evaluate_(u); }); \ } // ADL will find these operators. UN_OP_FOR_NULLARY(!) UN_OP_FOR_NULLARY(-) #undef UN_OP_FOR_NULLARY BIN_OP_FOR_NULLARY(+) BIN_OP_FOR_NULLARY(-) BIN_OP_FOR_NULLARY(*) BIN_OP_FOR_NULLARY(/) BIN_OP_FOR_NULLARY(%) BIN_OP_FOR_NULLARY(<) BIN_OP_FOR_NULLARY(<=) BIN_OP_FOR_NULLARY(==) BIN_OP_FOR_NULLARY(!=) BIN_OP_FOR_NULLARY(>=) BIN_OP_FOR_NULLARY(>) BIN_OP_FOR_NULLARY(||) BIN_OP_FOR_NULLARY(&&) BIN_OP_FOR_NULLARY(^) #undef BIN_OP_FOR_NULLARY // Unary template class IsUnaryOrWildcard : public std::false_type {}; template <> class IsUnaryOrWildcard : public std::true_type {}; template class IsUnaryOrWildcard> : public std::true_type {}; template constexpr auto isUnaryOrWildcardV = IsUnaryOrWildcard>::value; // unary is an alias of meet. template constexpr auto unary(T&& t) { return meet(std::forward(t)); } #define UN_OP_FOR_UNARY(op) \ template , bool> = true> \ constexpr auto operator op(T const& t) { \ return unary([&](auto&& arg) constexpr { return op evaluate_(t, arg); }); \ } #define BIN_OP_FOR_UNARY(op) \ template || isUnaryOrWildcardV, bool> = true> \ constexpr auto operator op(T const& t, U const& u) { \ return unary([&](auto&& arg) constexpr { return evaluate_(t, arg) op evaluate_(u, arg); }); \ } UN_OP_FOR_UNARY(!) UN_OP_FOR_UNARY(-) #undef UN_OP_FOR_UNARY BIN_OP_FOR_UNARY(+) BIN_OP_FOR_UNARY(-) BIN_OP_FOR_UNARY(*) BIN_OP_FOR_UNARY(/) BIN_OP_FOR_UNARY(%) BIN_OP_FOR_UNARY(<) BIN_OP_FOR_UNARY(<=) BIN_OP_FOR_UNARY(==) BIN_OP_FOR_UNARY(!=) BIN_OP_FOR_UNARY(>=) BIN_OP_FOR_UNARY(>) BIN_OP_FOR_UNARY(||) BIN_OP_FOR_UNARY(&&) BIN_OP_FOR_UNARY(^) #undef BIN_OP_FOR_UNARY } // namespace impl using impl::expr; } // namespace matchit #endif // MATCHIT_EXPRESSION_H #ifndef MATCHIT_PATTERNS_H #define MATCHIT_PATTERNS_H #include #include #include #include #include #if !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) #define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 0 #endif // !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) namespace matchit { namespace impl { template class Subrange { I mBegin; S mEnd; public: constexpr Subrange(const I begin, const S end) : mBegin { begin }, mEnd { end } {} constexpr Subrange(const Subrange& other) : mBegin { other.begin() }, mEnd { other.end() } {} Subrange& operator=(const Subrange& other) { mBegin = other.begin(); mEnd = other.end(); return *this; } size_t size() const { return static_cast(std::distance(mBegin, mEnd)); } auto begin() const { return mBegin; } auto end() const { return mEnd; } }; template constexpr auto makeSubrange(I begin, S end) { return Subrange { begin, end }; } template class IterUnderlyingType { public: using beginT = decltype(std::begin(std::declval())); using endT = decltype(std::end(std::declval())); }; // force array iterators fallback to pointers. template class IterUnderlyingType> { public: using beginT = decltype(&*std::begin(std::declval&>())); using endT = beginT; }; // force array iterators fallback to pointers. template class IterUnderlyingType> { public: using beginT = decltype(&*std::begin(std::declval&>())); using endT = beginT; }; template using SubrangeT = Subrange::beginT, typename IterUnderlyingType::endT>; template bool operator==(const Subrange& lhs, const Subrange& rhs) { using std::operator==; return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template auto operator==(const std::pair& t, const std::pair& u) { return t.first == u.first && t.second == u.second; } template class WithinTypes { public: constexpr static auto value = (std::is_same_v || ...); }; template class PrependUnique; template class PrependUnique> { constexpr static auto unique = !WithinTypes::value; public: using type = std::conditional_t, std::tuple>; }; template using PrependUniqueT = typename PrependUnique::type; template class Unique; template using UniqueT = typename Unique>::type; template <> class Unique> { public: using type = std::tuple<>; }; template class Unique> { public: using type = PrependUniqueT>; }; static_assert(std::is_same_v, UniqueT>); static_assert(std::is_same_v, int32_t>, UniqueT, int32_t>>); using std::get; namespace detail { template constexpr decltype(auto) subtupleImpl(Tuple&& t, std::index_sequence) { return std::forward_as_tuple(get(std::forward(t))...); } } // namespace detail // [start, end) template constexpr decltype(auto) subtuple(Tuple&& t) { constexpr auto tupleSize = std::tuple_size_v>; static_assert(start <= end); static_assert(end <= tupleSize); return detail::subtupleImpl(std::forward(t), std::make_index_sequence {}); } template constexpr decltype(auto) drop(Tuple&& t) { constexpr auto tupleSize = std::tuple_size_v>; static_assert(start <= tupleSize); return subtuple(std::forward(t)); } template constexpr decltype(auto) take(Tuple&& t) { constexpr auto tupleSize = std::tuple_size_v>; static_assert(len <= tupleSize); return subtuple<0, len>(std::forward(t)); } template constexpr decltype(auto) apply_(F&& f, Tuple&& t) { return std::apply(std::forward(f), drop<0>(std::forward(t))); } // as constexpr template constexpr std::invoke_result_t invoke_(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_v) { return std::apply(std::forward(f), std::forward_as_tuple(std::forward(args)...)); } template struct decayArray { private: typedef std::remove_reference_t U; public: using type = std::conditional_t, std::remove_extent_t*, T>; }; template using decayArrayT = typename decayArray::type; static_assert(std::is_same_v, int32_t*>); static_assert(std::is_same_v, const int32_t*>); static_assert(std::is_same_v, const int32_t&>); template struct AddConstToPointer { using type = std::conditional_t, T, std::add_pointer_t>>>; }; template using AddConstToPointerT = typename AddConstToPointer::type; static_assert(std::is_same_v, const void*>); static_assert(std::is_same_v, int32_t>); template using InternalPatternT = std::remove_reference_t>>; template class PatternTraits; template class PatternPairsRetType { public: using RetType = std::common_type_t; }; enum class IdProcess : int32_t { kCANCEL, kCONFIRM }; template constexpr void processId(const Pattern& pattern, int32_t depth, IdProcess idProcess) { PatternTraits::processIdImpl(pattern, depth, idProcess); } template class Variant; template class Variant> { public: using type = std::variant; }; template using UniqVariant = typename Variant>::type; template class Context { using ElementT = UniqVariant; using ContainerT = std::array; ContainerT mMemHolder; size_t mSize = 0; public: template constexpr void emplace_back(T&& t) { mMemHolder[mSize] = std::forward(t); ++mSize; } constexpr auto back() -> ElementT& { return mMemHolder[mSize - 1]; } }; template <> class Context<> {}; template class ContextTrait; template class ContextTrait> { public: using ContextT = Context; }; template constexpr auto matchPattern(Value&& value, const Pattern& pattern, int32_t depth, ConctextT& context) { const auto result = PatternTraits::matchPatternImpl(std::forward(value), pattern, depth, context); const auto process = result ? IdProcess::kCONFIRM : IdProcess::kCANCEL; processId(pattern, depth, process); return result; } template class PatternPair { public: using RetType = std::invoke_result_t; using PatternT = Pattern; constexpr PatternPair(const Pattern& pattern, const Func& func) : mPattern { pattern }, mHandler { func } {} template constexpr bool matchValue(Value&& value, ContextT& context) const { return matchPattern(std::forward(value), mPattern, /*depth*/ 0, context); } constexpr auto execute() const { return mHandler(); } private: const Pattern mPattern; std::conditional_t, const Func&, const Func> mHandler; }; template class PostCheck; template class When { public: Pred mPred; }; template constexpr auto when(Pred&& pred) { auto p = toNullary(pred); return When { p }; } template class PatternHelper { public: constexpr explicit PatternHelper(const Pattern& pattern) : mPattern { pattern } {} template constexpr auto operator=(Func&& func) { auto f = toNullary(func); return PatternPair { mPattern, f }; } template constexpr auto operator|(const When& w) { return PatternHelper>(PostCheck(mPattern, w.mPred)); } private: const Pattern mPattern; }; template class Ds; template constexpr auto ds(const Patterns&... patterns) -> Ds; template class OooBinder; class PatternPipable { public: template // ReSharper disable once CppDFAUnreachableFunctionCall constexpr auto operator|(const Pattern& p) const { return PatternHelper { p }; } template constexpr auto operator|(const T* p) const { return PatternHelper { p }; } template constexpr auto operator|(const OooBinder& p) const { return operator|(ds(p)); } }; constexpr PatternPipable is {}; template class PatternTraits { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static auto matchPatternImpl(Value&& value, const Pattern& pattern, int32_t /* depth */, ContextT& /*context*/) { return pattern == std::forward(value); } constexpr static void processIdImpl(const Pattern&, int32_t /*depth*/, IdProcess) {} }; class Wildcard {}; constexpr Wildcard _; template <> class PatternTraits { using Pattern = Wildcard; public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static bool matchPatternImpl(Value&&, const Pattern&, int32_t, ContextT&) { return true; } constexpr static void processIdImpl(const Pattern&, int32_t /*depth*/, IdProcess) {} }; template class Or { public: constexpr explicit Or(const Patterns&... patterns) : mPatterns { patterns... } {} constexpr const auto& patterns() const { return mPatterns; } private: std::tuple...> mPatterns; }; template constexpr auto or_(const Patterns&... patterns) { return Or { patterns... }; } template class PatternTraits> { public: template using AppResultTuple = decltype(std::tuple_cat(typename PatternTraits::template AppResultTuple {}...)); constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(Value&& value, const Or& orPat, int32_t depth, ContextT& context) { constexpr auto patSize = sizeof...(Patterns); return std::apply( [&value, depth, &context](const auto&... patterns) { return (matchPattern(value, patterns, depth + 1, context) || ...); }, take(orPat.patterns()) ) || matchPattern(std::forward(value), get(orPat.patterns()), depth + 1, context); } constexpr static void processIdImpl(const Or& orPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](const Patterns&... patterns) { return (processId(patterns, depth, idProcess), ...); }, orPat.patterns() ); } }; template class Meet : public Pred { public: using Pred::operator(); }; template constexpr auto meet(const Pred& pred) { return Meet { pred }; } template class PatternTraits> { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static auto matchPatternImpl(Value&& value, const Meet& meetPat, int32_t /* depth */, ContextT&) { return meetPat(std::forward(value)); } constexpr static void processIdImpl(const Meet&, int32_t /*depth*/, IdProcess) {} }; template class App { public: constexpr App(Unary&& unary, const Pattern& pattern) : mUnary { std::forward(unary) }, mPattern { pattern } {} constexpr const auto& unary() const { return mUnary; } constexpr const auto& pattern() const { return mPattern; } private: const Unary mUnary; const InternalPatternT mPattern; }; template constexpr auto app(Unary&& unary, const Pattern& pattern) { return App { std::forward(unary), pattern }; } constexpr auto y = 1; static_assert(std::holds_alternative(std::variant { &y })); template class PatternTraits> { public: template using AppResult = std::invoke_result_t; // We store value for scalar types in Id and they can not be moved. So to // support constexpr. template using AppResultCurTuple = std::conditional_t< std::is_lvalue_reference_v> #if NO_SCALAR_REFERENCES_USED_IN_PATTERNS || std::is_scalar_v> #endif // NO_SCALAR_REFERENCES_USED_IN_PATTERNS , std::tuple<>, std::tuple>>>; template using AppResultTuple = decltype(std::tuple_cat( std::declval>(), std::declval::template AppResultTuple>>() )); constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value&& value, const App& appPat, const int32_t depth, ContextT& context) { if constexpr (std::is_same_v, std::tuple<>>) { return matchPattern( std::forward>(invoke_(appPat.unary(), value)), appPat.pattern(), depth + 1, context ); } else { context.emplace_back(invoke_(appPat.unary(), value)); decltype(auto) result = get>>(context.back()); return matchPattern(std::forward>(result), appPat.pattern(), depth + 1, context); } } constexpr static void processIdImpl(const App& appPat, int32_t depth, IdProcess idProcess) { return processId(appPat.pattern(), depth, idProcess); } }; template class And { public: constexpr explicit And(const Patterns&... patterns) : mPatterns { patterns... } {} constexpr const auto& patterns() const { return mPatterns; } private: std::tuple...> mPatterns; }; template constexpr auto and_(const Patterns&... patterns) { return And { patterns... }; } template class NbIdInTuple; template class NbIdInTuple> { public: constexpr static auto nbIdV = (PatternTraits>::nbIdV + ... + 0); }; template class PatternTraits> { public: template using AppResultTuple = decltype(std::tuple_cat(std::declval::template AppResultTuple>()...)); constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(Value&& value, const And& andPat, int32_t depth, ContextT& context) { constexpr auto patSize = sizeof...(Patterns); const auto exceptLast = std::apply( [&value, depth, &context](const auto&... patterns) { return (matchPattern(value, patterns, depth + 1, context) && ...); }, take(andPat.patterns()) ); // No Id in patterns except the last one. if constexpr (NbIdInTuple(andPat.patterns()))>>::nbIdV == 0) { return exceptLast && matchPattern(std::forward(value), get(andPat.patterns()), depth + 1, context); } else { return exceptLast && matchPattern(value, get(andPat.patterns()), depth + 1, context); } } constexpr static void processIdImpl(const And& andPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](const Patterns&... patterns) { return (processId(patterns, depth, idProcess), ...); }, andPat.patterns() ); } }; template class Not { public: explicit Not(const Pattern& pattern) : mPattern { pattern } {} const auto& pattern() const { return mPattern; } private: InternalPatternT mPattern; }; template constexpr auto not_(const Pattern& pattern) { return Not { pattern }; } template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value&& value, const Not& notPat, const int32_t depth, ContextT& context) { return !matchPattern(std::forward(value), notPat.pattern(), depth + 1, context); } constexpr static void processIdImpl(const Not& notPat, int32_t depth, IdProcess idProcess) { processId(notPat.pattern(), depth, idProcess); } }; template > struct StorePointer : std::false_type {}; template using ValueVariant = std::conditional_t< std::is_lvalue_reference_v, UniqVariant*>, std::conditional_t< std::is_rvalue_reference_v, UniqVariant, std::remove_reference_t*>, std::conditional_t< std::is_abstract_v>, UniqVariant*, const std::remove_reference_t*>, UniqVariant< std::remove_reference_t, std::remove_reference_t*, const std::remove_reference_t*>>>>; template struct StorePointer< Type, Value, std::void_t&>() = &std::declval())>> : std::is_reference // need to double check this condition. to loosen it. {}; static_assert(!StorePointer::value); static_assert(StorePointer::value); static_assert(StorePointer::value); static_assert(StorePointer::value); static_assert(StorePointer, const std::tuple&>::value); template class Overload : public Ts... { public: using Ts::operator()...; }; template constexpr auto overload(Ts&&... ts) { return Overload { ts... }; } template class OooBinder; class Ooo; template class IdTraits { public: constexpr static auto #if defined(__has_feature) #if __has_feature(address_sanitizer) __attribute__((no_sanitize_address)) #endif #endif equal(Type const& lhs, Type const& rhs) { return lhs == rhs; } }; template class IdBlockBase { int32_t mDepth; protected: ValueVariant mVariant; public: constexpr IdBlockBase() : mDepth {}, mVariant {} {} constexpr auto& variant() { return mVariant; } constexpr void reset(const int32_t depth) { if (mDepth - depth >= 0) { mVariant = {}; mDepth = depth; } } constexpr void confirm(const int32_t depth) { if (mDepth > depth || mDepth == 0) { assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0); mDepth = depth; } } }; constexpr IdBlockBase dummy; template class IdBlock : public IdBlockBase { public: constexpr auto hasValue() const { return std::visit( overload( [](const Type&) { return true; }, [](const Type*) { return true; }, // [](Type *) // { return true; }, [](const std::monostate&) { return false; } ), IdBlockBase::mVariant ); } constexpr decltype(auto) get() const { return std::visit( overload( [](const Type& v) -> const Type& { return v; }, [](const Type* p) -> const Type& { return *p; }, [](Type* p) -> const Type& { return *p; }, [](const std::monostate&) -> const Type& { throw std::logic_error("invalid state!"); } ), IdBlockBase::mVariant ); } }; template class IdBlock : public IdBlock {}; template class IdBlock : public IdBlockBase { public: constexpr auto hasValue() const { return std::visit( overload([](Type*) { return true; }, [](const std::monostate&) { return false; }), IdBlockBase::mVariant ); } constexpr decltype(auto) get() { return std::visit( overload( [](Type* v) -> Type& { if (v == nullptr) { throw std::logic_error("Trying to dereference a nullptr!"); } return *v; }, [](std::monostate&) -> Type& { throw std::logic_error("Invalid state!"); } ), IdBlockBase::mVariant ); } }; template class IdBlock : public IdBlockBase { public: constexpr auto hasValue() const { return std::visit( overload( [](const Type&) { return true; }, [](Type*) { return true; }, [](const std::monostate&) { return false; } ), IdBlockBase::mVariant ); } constexpr decltype(auto) get() { return std::visit( overload( [](Type& v) -> Type& { return v; }, [](Type* v) -> Type& { if (v == nullptr) { throw std::logic_error("Trying to dereference a nullptr!"); } return *v; }, [](std::monostate&) -> Type& { throw std::logic_error("Invalid state!"); } ), IdBlockBase::mVariant ); } }; template class IdUtil { public: template constexpr static auto bindValue(ValueVariant& v, Value&& value, std::false_type /* StorePointer */) { // for constexpr v = ValueVariant { std::forward(value) }; } template constexpr static auto bindValue(ValueVariant& v, Value&& value, std::true_type /* StorePointer */) { v = ValueVariant { &value }; } }; template class Id { private: using BlockT = IdBlock; using BlockVT = std::variant; BlockVT mBlock = BlockT {}; constexpr decltype(auto) internalValue() const { return block().get(); } public: constexpr Id() = default; constexpr Id(const Id& id) { mBlock = BlockVT { &id.block() }; } // non-const to inform users not to mark Id as const. template constexpr auto at(Pattern&& pattern) { return and_(pattern, *this); } // non-const to inform users not to mark Id as const. constexpr auto at(const Ooo&) { return OooBinder { *this }; } constexpr BlockT& block() const { return std::visit( overload([](BlockT& v) -> BlockT& { return v; }, [](BlockT* p) -> BlockT& { return *p; }), // constexpr does not allow mutable, we use const_cast // instead. Never declare Id as const. const_cast(mBlock) ); } template constexpr auto matchValue(Value&& v) const { if (hasValue()) { return IdTraits>::equal(internalValue(), v); } IdUtil::bindValue(block().variant(), std::forward(v), StorePointer {}); return true; } constexpr void reset(int32_t depth) const { return block().reset(depth); } constexpr void confirm(int32_t depth) const { return block().confirm(depth); } constexpr bool hasValue() const { return block().hasValue(); } // non-const to inform users not to mark Id as const. constexpr decltype(auto) get() { return block().get(); } // non-const to inform users not to mark Id as const. constexpr decltype(auto) operator*() { return get(); } }; template class PatternTraits> { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = true; template constexpr static auto matchPatternImpl(Value&& value, const Id& idPat, int32_t /* depth */, ContextT&) { return idPat.matchValue(std::forward(value)); } constexpr static void processIdImpl(const Id& idPat, int32_t depth, const IdProcess idProcess) { switch (idProcess) { case IdProcess::kCANCEL: idPat.reset(depth); break; case IdProcess::kCONFIRM: idPat.confirm(depth); break; } } }; template class Ds { public: constexpr explicit Ds(const Patterns&... patterns) : mPatterns { patterns... } {} constexpr const auto& patterns() const { return mPatterns; } using Type = std::tuple...>; private: Type mPatterns; }; template constexpr auto ds(const Patterns&... patterns) -> Ds { return Ds { patterns... }; } template class OooBinder { Id mId; public: explicit OooBinder(const Id& id) : mId { id } {} decltype(auto) binder() const { return mId; } }; class Ooo { public: template constexpr auto operator()(Id id) const { return OooBinder { id }; } }; constexpr Ooo ooo; template <> class PatternTraits { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = false; template constexpr static auto matchPatternImpl(Value&&, Ooo, int32_t /*depth*/, ContextT&) { return true; } constexpr static void processIdImpl(Ooo, int32_t /*depth*/, IdProcess) {} }; template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value&& value, const OooBinder& oooBinderPat, const int32_t depth, ContextT& context) { return matchPattern(std::forward(value), oooBinderPat.binder(), depth + 1, context); } constexpr static void processIdImpl(const OooBinder& oooBinderPat, int32_t depth, IdProcess idProcess) { processId(oooBinderPat.binder(), depth, idProcess); } }; template class IsOoo : public std::false_type {}; template <> class IsOoo : public std::true_type {}; template class IsOooBinder : public std::false_type {}; template class IsOooBinder> : public std::true_type {}; template constexpr auto isOooBinderV = IsOooBinder>::value; template constexpr auto isOooOrBinderV = IsOoo>::value || isOooBinderV; template constexpr auto nbOooOrBinderV = ((isOooOrBinderV ? 1 : 0) + ... + 0); static_assert(nbOooOrBinderV == 2); template constexpr size_t findOooIdxImpl(std::index_sequence) { return ((isOooOrBinderV(std::declval()))> ? I : 0) + ...); } template constexpr size_t findOooIdx() { return findOooIdxImpl(std::make_index_sequence>> {}); } static_assert(isOooOrBinderV); static_assert(isOooOrBinderV>); static_assert(findOooIdx, const char*>>() == 1); static_assert(findOooIdx>() == 1); using std::get; template < std::size_t valueStartIdx, std::size_t patternStartIdx, std::size_t... I, typename ValueTuple, typename PatternTuple, typename ContextT> constexpr decltype(auto) matchPatternMultipleImpl(ValueTuple&& valueTuple, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence) { const auto func = [&](T&& value, auto&& pattern) { return matchPattern(std::forward(value), pattern, depth + 1, context); }; static_cast(func); return ( func( get(std::forward(valueTuple)), std::get(patternTuple) ) && ... ); } template < std::size_t valueStartIdx, std::size_t patternStartIdx, std::size_t size, typename ValueTuple, typename PatternTuple, typename ContextT> constexpr decltype(auto) matchPatternMultiple(ValueTuple&& valueTuple, PatternTuple&& patternTuple, int32_t depth, ContextT& context) { return matchPatternMultipleImpl( std::forward(valueTuple), patternTuple, depth, context, std::make_index_sequence {} ); } template < std::size_t patternStartIdx, std::size_t... I, typename RangeBegin, typename PatternTuple, typename ContextT> constexpr decltype(auto) matchPatternRangeImpl(RangeBegin&& rangeBegin, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence) { const auto func = [&](T&& value, auto&& pattern) { return matchPattern(std::forward(value), pattern, depth + 1, context); }; static_cast(func); // Fix Me, avoid call next from begin every time. return (func(*std::next(rangeBegin, static_cast(I)), std::get(patternTuple)) && ...); } template < std::size_t patternStartIdx, std::size_t size, typename ValueRangeBegin, typename PatternTuple, typename ContextT> constexpr decltype(auto) matchPatternRange( ValueRangeBegin&& valueRangeBegin, PatternTuple&& patternTuple, int32_t depth, ContextT& context ) { return matchPatternRangeImpl( valueRangeBegin, patternTuple, depth, context, std::make_index_sequence {} ); } template class IndexedTypes; template class IndexedTypes, Tuple> { public: using type = std::tuple(std::declval()))>...>; }; template class SubTypes { constexpr static auto tupleSize = std::tuple_size_v>; static_assert(start <= end); static_assert(end <= tupleSize); using Indices = std::make_index_sequence; public: using type = typename IndexedTypes::type; }; template using SubTypesT = typename SubTypes::type; static_assert(std::is_same_v< std::tuple, SubTypesT<3, 4, std::tuple>>); static_assert(std::is_same_v, SubTypesT<0, 1, std::tuple>>); static_assert(std::is_same_v, SubTypesT<1, 1, std::tuple>>); static_assert(std::is_same_v< std::tuple, SubTypesT<2, 4, std::tuple>>); template class IsArray : public std::false_type {}; template class IsArray> : public std::true_type {}; template constexpr auto isArrayV = IsArray>::value; template > struct IsTupleLike : std::false_type {}; template // ReSharper disable once CppUseTypeTraitAlias struct IsTupleLike::value)>> : std::true_type {}; template constexpr auto isTupleLikeV = IsTupleLike>::value; static_assert(isTupleLikeV>); static_assert(!isTupleLikeV); template > struct IsRange : std::false_type {}; template struct IsRange< Value, std::void_t())), decltype(std::end(std::declval()))>> : std::true_type {}; template constexpr auto isRangeV = IsRange>::value; static_assert(!isRangeV>); static_assert(isRangeV>); template class PatternTraits> { constexpr static auto nbOooOrBinder = nbOooOrBinderV; static_assert(nbOooOrBinder == 0 || nbOooOrBinder == 1); public: template class PairPV; template class PairPV, std::tuple> { public: using type = decltype(std::tuple_cat(std::declval::template AppResultTuple>()...)); }; template class AppResultForTupleHelper; template class AppResultForTupleHelper<0, std::tuple> { public: using type = decltype(std::tuple_cat(std::declval::template AppResultTuple>()...) ); }; template class AppResultForTupleHelper<1, std::tuple> { constexpr static auto idxOoo = findOooIdx::Type>(); using Ps0 = SubTypesT<0, idxOoo, std::tuple>; using Vs0 = SubTypesT<0, idxOoo, std::tuple>; constexpr static auto isBinder = isOooBinderV>>; // <0, ...int32_t> to workaround compile failure for std::tuple<>. using ElemT = std::tuple_element_t<0, std::tuple..., int32_t>>; constexpr static int64_t diff = static_cast(sizeof...(Values) - sizeof...(Patterns)); constexpr static size_t clippedDiff = static_cast(diff > 0 ? diff : 0); using OooResultTuple = std::conditional_t>>, std::tuple<>>; using FirstHalfTuple = typename PairPV::type; using Ps1 = SubTypesT>; constexpr static auto vs1Start = static_cast(static_cast(idxOoo) + 1 + diff); using Vs1 = SubTypesT>; using SecondHalfTuple = typename PairPV::type; public: using type = decltype(std::tuple_cat( std::declval(), std::declval(), std::declval() )); }; template using AppResultForTuple = typename AppResultForTupleHelper(std::declval()))>::type; template using RangeTuple = std::conditional_t>, std::tuple<>>; template using AppResultForRangeType = decltype(std::tuple_cat( std::declval>(), std::declval< typename PatternTraits::template AppResultTuple()))>>( )... )); template > class AppResultHelper; template class AppResultHelper>> { public: using type = AppResultForTuple; }; template class AppResultHelper && isRangeV>> { public: using type = AppResultForRangeType; }; template using AppResultTuple = typename AppResultHelper::type; constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(ValueTuple&& valueTuple, const Ds& dsPat, int32_t depth, ContextT& context) -> std::enable_if_t, bool> { if constexpr (nbOooOrBinder == 0) { return std::apply( [&valueTuple, depth, &context](const auto&... patterns) { return apply_( [depth, &context, &patterns...](T&&... values) constexpr { static_assert(sizeof...(patterns) == sizeof...(values)); return (matchPattern(std::forward(values), patterns, depth + 1, context) && ...); }, valueTuple ); }, dsPat.patterns() ); } else if constexpr (nbOooOrBinder == 1) { constexpr auto idxOoo = findOooIdx::Type>(); constexpr auto isBinder = isOooBinderV>>; constexpr auto isArray = isArrayV; auto result = matchPatternMultiple<0, 0, idxOoo>(std::forward(valueTuple), dsPat.patterns(), depth, context); constexpr auto valLen = std::tuple_size_v>; constexpr auto patLen = sizeof...(Patterns); if constexpr (isArray) { if constexpr (isBinder) { const auto rangeSize = static_cast(valLen - (patLen - 1)); context.emplace_back(makeSubrange(&valueTuple[idxOoo], &valueTuple[idxOoo] + rangeSize)); using type = decltype(makeSubrange(&valueTuple[idxOoo], &valueTuple[idxOoo] + rangeSize)); result = result && matchPattern(std::get(context.back()), std::get(dsPat.patterns()), depth, context); } } else { static_assert(!isBinder); } return result && matchPatternMultiple( std::forward(valueTuple), dsPat.patterns(), depth, context ); } return; } template constexpr static auto matchPatternImpl(ValueRange&& valueRange, const Ds& dsPat, int32_t depth, ContextT& context) -> std::enable_if_t && isRangeV, bool> { static_assert(nbOooOrBinder == 0 || nbOooOrBinder == 1); constexpr auto nbPat = sizeof...(Patterns); if constexpr (nbOooOrBinder == 0) { // size mismatch for dynamic array is not an error; if (valueRange.size() != nbPat) { return false; } return matchPatternRange<0, nbPat>(std::begin(valueRange), dsPat.patterns(), depth, context); } else if constexpr (nbOooOrBinder == 1) { if (valueRange.size() < nbPat - 1) { return false; } constexpr auto idxOoo = findOooIdx::Type>(); constexpr auto isBinder = isOooBinderV>>; auto result = matchPatternRange<0, idxOoo>(std::begin(valueRange), dsPat.patterns(), depth, context); const auto valLen = valueRange.size(); constexpr auto patLen = sizeof...(Patterns); const auto beginOoo = std::next(std::begin(valueRange), idxOoo); if constexpr (isBinder) { const auto rangeSize = static_cast(valLen - (patLen - 1)); const auto end = std::next(beginOoo, rangeSize); context.emplace_back(makeSubrange(beginOoo, end)); using type = decltype(makeSubrange(beginOoo, end)); result = result && matchPattern(std::get(context.back()), std::get(dsPat.patterns()), depth, context); } const auto beginAfterOoo = std::next(beginOoo, static_cast(valLen - patLen + 1)); return result && matchPatternRange(beginAfterOoo, dsPat.patterns(), depth, context); } return; } constexpr static void processIdImpl(const Ds& dsPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](auto&&... patterns) { return (processId(patterns, depth, idProcess), ...); }, dsPat.patterns() ); } }; static_assert(std::is_same_v< PatternTraits>>>>::AppResultTuple< const std::array>, std::tuple>>); static_assert(std::is_same_v< PatternTraits>, matchit::impl::Id>>::AppResultTuple< const std::array>, std::tuple>>); static_assert(std::is_same_v< PatternTraits>, matchit::impl::Id>>::AppResultTuple< std::array>, std::tuple>>); template class PostCheck { public: constexpr explicit PostCheck(const Pattern& pattern, const Pred& pred) : mPattern { pattern }, mPred { pred } {} constexpr bool check() const { return mPred(); } constexpr const auto& pattern() const { return mPattern; } private: const Pattern mPattern; const Pred mPred; }; template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; template constexpr static auto matchPatternImpl( Value&& value, const PostCheck& postCheck, const int32_t depth, ContextT& context ) { return matchPattern(std::forward(value), postCheck.pattern(), depth + 1, context) && postCheck.check(); } constexpr static void processIdImpl(const PostCheck& postCheck, int32_t depth, IdProcess idProcess) { processId(postCheck.pattern(), depth, idProcess); } }; static_assert(std::is_same_v::AppResultTuple, std::tuple<>>); static_assert(std::is_same_v::AppResultTuple, std::tuple<>>); constexpr auto x = [](auto&& t) { return t; }; static_assert(std::is_same_v< PatternTraits>::AppResultTuple>, std::tuple>>); static_assert(PatternTraits>>::nbIdV == 0); static_assert(PatternTraits>>>::nbIdV == 1); static_assert(PatternTraits, Id>>::nbIdV == 2); static_assert(PatternTraits, Id>>::nbIdV == 2); static_assert(PatternTraits>::nbIdV == 0); template constexpr auto matchPatterns(Value&& value, const PatternPairs&... patterns) { using RetType = typename PatternPairsRetType::RetType; using TypeTuple = decltype(std::tuple_cat( std::declval::template AppResultTuple>()... )); // expression, has return value. if constexpr (!std::is_same_v) { constexpr auto func = [](const auto& pattern, auto&& val, RetType& result) constexpr -> bool { if (auto context = typename ContextTrait::ContextT {}; pattern.matchValue(std::forward(val), context)) { result = pattern.execute(); processId(pattern, 0, IdProcess::kCANCEL); return true; } return false; }; RetType result {}; const bool matched = (func(patterns, value, result) || ...); if (!matched) { throw std::logic_error { "Error: no patterns got matched!" }; } static_cast(matched); return result; } else // statement, no return value, mismatching all patterns is not an error. { const auto func = [](const auto& pattern, auto&& val) -> bool { if (auto context = typename ContextTrait::ContextT {}; pattern.matchValue(std::forward(val), context)) { pattern.execute(); processId(pattern, 0, IdProcess::kCANCEL); return true; } return false; }; const bool matched = (func(patterns, value) || ...); static_cast(matched); } } } // namespace impl // export symbols using impl::_; using impl::and_; using impl::app; using impl::ds; using impl::Id; using impl::is; using impl::meet; using impl::not_; using impl::ooo; using impl::or_; using impl::Subrange; using impl::SubrangeT; using impl::when; } // namespace matchit #endif // MATCHIT_PATTERNS_H #ifndef MATCHIT_UTILITY_H #define MATCHIT_UTILITY_H #include namespace matchit { namespace impl { template constexpr auto cast = [](auto&& input) { return static_cast(input); }; constexpr auto deref = [](auto&& x) -> decltype(*x)& { return *x; }; constexpr auto some = [](const auto pat) { return and_(app(cast, true), app(deref, pat)); }; constexpr auto none = app(cast, false); template > struct ViaGetIf : std::false_type {}; using std::get_if; template struct ViaGetIf(std::declval()))>> : std::true_type {}; template constexpr auto viaGetIfV = ViaGetIf::value; static_assert(viaGetIfV>); template class AsPointer { static_assert(!std::is_reference_v); public: template >>* = nullptr> constexpr auto operator()(Variant&& v) const { return get_if(std::addressof(v)); } // template to disable implicit cast to std::any template , std::any>>* = nullptr> constexpr auto operator()(A&& a) const { return std::any_cast(std::addressof(a)); } // cast to base class template && std::is_base_of_v>* = nullptr> constexpr auto operator()(const D& d) const -> decltype(static_cast(std::addressof(d))) { return static_cast(std::addressof(d)); } // No way to handle rvalue to save copy in this class. Need to define some in another way to handle this. // cast to base class template && std::is_base_of_v>* = nullptr> constexpr auto operator()(D& d) const -> decltype(static_cast(std::addressof(d))) { return static_cast(std::addressof(d)); } // cast to derived class template && std::is_base_of_v>* = nullptr> constexpr auto operator()(const B& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } // cast to derived class template && std::is_base_of_v>* = nullptr> constexpr auto operator()(B& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } constexpr auto operator()(const T& b) const { return std::addressof(b); } constexpr auto operator()(T& b) const { return std::addressof(b); } }; static_assert(std::is_invocable_v, int>); static_assert(std::is_invocable_v>, std::tuple>); template constexpr AsPointer asPointer; template constexpr auto as = [](const auto pat) { return app(asPointer, some(pat)); }; template constexpr auto matched(Value&& v, Pattern&& p) { return match(std::forward(v))( is | std::forward(p) = [] { return true; }, is | _ = [] { return false; } ); } constexpr auto dsVia = [](auto... members) { return [members...](auto... pats) { return and_(app(members, pats)...); }; }; template constexpr auto asDsVia = [](auto... members) { return [members...](auto... pats) { return as(and_(app(members, pats)...)); }; }; } // namespace impl using impl::as; using impl::asDsVia; using impl::dsVia; using impl::matched; using impl::none; using impl::some; // NOLINTEND(*-identifier-naming) } // namespace matchit #endif // MATCHIT_UTILITY_H #endif // MATCHIT_H