Start fixing up Matchers: namespaces, composition ops

This commit also forbids composing lvalues of composed matchers, as
per previous deprecation notice. I do not expect this to be contentious
in practice, because there was a bug in that usage for years, and
nobody complained.
This commit is contained in:
Martin Hořeňovský
2020-02-18 23:31:16 +01:00
parent a1be19aa1b
commit cf6575576f
22 changed files with 230 additions and 296 deletions

View File

@@ -26,7 +26,6 @@
#include <catch2/catch_compiler_capabilities.h>
#include <catch2/catch_string_manip.h>
#include <catch2/catch_capture_matchers.h>
#include <catch2/catch_generators.hpp>
#include <catch2/catch_generators_generic.hpp>
#include <catch2/catch_generators_specific.hpp>

View File

@@ -10,8 +10,6 @@
namespace Catch {
using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
// This is the general overload that takes a any string matcher
// There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
// the Equals matcher (so the header does not mention matchers)

View File

@@ -17,7 +17,7 @@ namespace Catch {
template<typename ArgT, typename MatcherT>
class MatchExpr : public ITransientExpression {
ArgT && m_arg;
MatcherT m_matcher;
MatcherT const& m_matcher;
StringRef m_matcherString;
public:
MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString )
@@ -37,7 +37,7 @@ namespace Catch {
}
};
using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
using StringMatcher = Matchers::MatcherBase<std::string>;
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString );

View File

@@ -9,7 +9,6 @@
namespace Catch {
namespace Matchers {
namespace Impl {
std::string MatcherUntypedBase::toString() const {
if( m_cachedToString.empty() )
@@ -19,10 +18,5 @@ namespace Matchers {
MatcherUntypedBase::~MatcherUntypedBase() = default;
} // namespace Impl
} // namespace Matchers
using namespace Matchers;
using Matchers::Impl::MatcherBase;
} // namespace Catch

View File

@@ -15,11 +15,11 @@
namespace Catch {
namespace Matchers {
namespace Impl {
template<typename ArgT> struct MatchAllOf;
template<typename ArgT> struct MatchAnyOf;
template<typename ArgT> struct MatchNotOf;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#endif
class MatcherUntypedBase {
public:
@@ -29,16 +29,11 @@ namespace Matchers {
std::string toString() const;
protected:
virtual ~MatcherUntypedBase();
virtual ~MatcherUntypedBase(); // = default;
virtual std::string describe() const = 0;
mutable std::string m_cachedToString;
};
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#endif
template<typename ObjectT>
struct MatcherMethod {
virtual bool match( ObjectT const& arg ) const = 0;
@@ -58,16 +53,19 @@ namespace Matchers {
#endif
template<typename T>
struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {};
MatchAllOf<T> operator && ( MatcherBase const& other ) const;
MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
MatchNotOf<T> operator ! () const;
};
namespace Detail {
template<typename ArgT>
struct MatchAllOf : MatcherBase<ArgT> {
MatchAllOf() = default;
MatchAllOf(MatchAllOf const&) = delete;
MatchAllOf& operator=(MatchAllOf const&) = delete;
MatchAllOf(MatchAllOf&&) = default;
MatchAllOf& operator=(MatchAllOf&&) = default;
bool match( ArgT const& arg ) const override {
for( auto matcher : m_matchers ) {
if (!matcher->match(arg))
@@ -91,16 +89,35 @@ namespace Matchers {
return description;
}
MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
auto copy(*this);
copy.m_matchers.push_back( &other );
return copy;
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
lhs.m_matchers.push_back(&rhs);
return std::move(lhs);
}
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return std::move(rhs);
}
private:
std::vector<MatcherBase<ArgT> const*> m_matchers;
};
//! lvalue overload is intentionally deleted, users should
//! not be trying to compose stored composition matchers
template<typename ArgT>
MatchAllOf<ArgT> operator&& (MatchAllOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
//! lvalue overload is intentionally deleted, users should
//! not be trying to compose stored composition matchers
template<typename ArgT>
MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
template<typename ArgT>
struct MatchAnyOf : MatcherBase<ArgT> {
MatchAnyOf() = default;
MatchAnyOf(MatchAnyOf const&) = delete;
MatchAnyOf& operator=(MatchAnyOf const&) = delete;
MatchAnyOf(MatchAnyOf&&) = default;
MatchAnyOf& operator=(MatchAnyOf&&) = default;
bool match( ArgT const& arg ) const override {
for( auto matcher : m_matchers ) {
@@ -125,19 +142,34 @@ namespace Matchers {
return description;
}
MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
auto copy(*this);
copy.m_matchers.push_back( &other );
return copy;
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
lhs.m_matchers.push_back(&rhs);
return std::move(lhs);
}
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return std::move(rhs);
}
private:
std::vector<MatcherBase<ArgT> const*> m_matchers;
};
//! lvalue overload is intentionally deleted, users should
//! not be trying to compose stored composition matchers
template<typename ArgT>
MatchAnyOf<ArgT> operator|| (MatchAnyOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
//! lvalue overload is intentionally deleted, users should
//! not be trying to compose stored composition matchers
template<typename ArgT>
MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
template<typename ArgT>
struct MatchNotOf : MatcherBase<ArgT> {
MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
m_underlyingMatcher( underlyingMatcher )
{}
bool match( ArgT const& arg ) const override {
return !m_underlyingMatcher.match( arg );
@@ -146,29 +178,29 @@ namespace Matchers {
std::string describe() const override {
return "not " + m_underlyingMatcher.toString();
}
private:
MatcherBase<ArgT> const& m_underlyingMatcher;
};
template<typename T>
MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
return MatchAllOf<T>() && *this && other;
}
template<typename T>
MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
return MatchAnyOf<T>() || *this || other;
}
template<typename T>
MatchNotOf<T> MatcherBase<T>::operator ! () const {
return MatchNotOf<T>( *this );
}
} // namespace Detail
template <typename T>
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
return Detail::MatchAllOf<T>{} && lhs && rhs;
}
template <typename T>
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
return Detail::MatchAnyOf<T>{} || lhs || rhs;
}
template <typename T>
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
return Detail::MatchNotOf<T>{ matcher };
}
} // namespace Impl
} // namespace Matchers
using namespace Matchers;
using Matchers::Impl::MatcherBase;
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED

View File

@@ -2,8 +2,9 @@
namespace Catch {
namespace Matchers {
namespace Impl {
MatcherGenericBase::~MatcherGenericBase() {}
MatcherGenericBase::~MatcherGenericBase() = default;
namespace Detail {
std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) {
std::string description;
@@ -27,6 +28,7 @@ namespace Matchers {
description += " )";
return description;
}
}
} // namespace Detail
} // namespace Matchers
} // namespace Catch

View File

@@ -14,16 +14,12 @@
namespace Catch {
namespace Matchers {
namespace Impl {
struct MatcherGenericBase : MatcherUntypedBase {
virtual ~MatcherGenericBase(); // = default;
};
template<typename... MatcherTs> struct MatchAllOfGeneric;
template<typename... MatcherTs> struct MatchAnyOfGeneric;
template<typename MatcherT> struct MatchNotOfGeneric;
struct MatcherGenericBase : MatcherUntypedBase {
virtual ~MatcherGenericBase();
};
namespace Detail {
template<std::size_t N, std::size_t M>
std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) {
std::array<void const*, N + M> arr{};
@@ -63,7 +59,7 @@ namespace Matchers {
template<typename T>
using is_generic_matcher = std::is_base_of<
Catch::Matchers::Impl::MatcherGenericBase,
Catch::Matchers::MatcherGenericBase,
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>;
@@ -72,7 +68,7 @@ namespace Matchers {
template<typename T>
using is_matcher = std::is_base_of<
Catch::Matchers::Impl::MatcherUntypedBase,
Catch::Matchers::MatcherUntypedBase,
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>;
@@ -161,102 +157,101 @@ namespace Matchers {
MatcherT const& m_matcher;
};
} // namespace Detail
// compose only generic matchers
template<typename MatcherLHS, typename MatcherRHS>
typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherRHS>>::type
// FIXME: enable_if_t
// compose only generic matchers
template<typename MatcherLHS, typename MatcherRHS>
typename std::enable_if<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>::type
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
template<typename MatcherLHS, typename MatcherRHS>
typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>::type
template<typename MatcherLHS, typename MatcherRHS>
typename std::enable_if<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>::type
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
template<typename MatcherT>
typename std::enable_if<is_generic_matcher<MatcherT>::value, MatchNotOfGeneric<MatcherT>>::type
template<typename MatcherT>
typename std::enable_if<Detail::is_generic_matcher<MatcherT>::value, Detail::MatchNotOfGeneric<MatcherT>>::type
operator ! (MatcherT const& matcher) {
return MatchNotOfGeneric<MatcherT>{matcher};
}
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
}
// compose mixed generic and non-generic matchers
template<typename MatcherLHS, typename ArgRHS>
typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
// compose mixed generic and non-generic matchers
template<typename MatcherLHS, typename ArgRHS>
typename std::enable_if<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
template<typename ArgLHS, typename MatcherRHS>
typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
template<typename ArgLHS, typename MatcherRHS>
typename std::enable_if<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
template<typename MatcherLHS, typename ArgRHS>
typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
template<typename MatcherLHS, typename ArgRHS>
typename std::enable_if<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
template<typename ArgLHS, typename MatcherRHS>
typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
template<typename ArgLHS, typename MatcherRHS>
typename std::enable_if<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
return {lhs, rhs};
}
return { lhs, rhs };
}
// avoid deep nesting of matcher templates
template<typename... MatchersLHS, typename... MatchersRHS>
MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>
operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) {
return MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
// avoid deep nesting of matcher templates
template<typename... MatchersLHS, typename... MatchersRHS>
Detail::MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>
operator && (Detail::MatchAllOfGeneric<MatchersLHS...>&& lhs, Detail::MatchAllOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>{Detail::array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename MatcherRHS>
typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatchersLHS..., MatcherRHS>>::type
operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) {
return MatchAllOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
template<typename... MatchersLHS, typename MatcherRHS>
typename std::enable_if<Detail::is_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatchersLHS..., MatcherRHS>>::type
operator && (Detail::MatchAllOfGeneric<MatchersLHS...>&& lhs, MatcherRHS const& rhs) {
return Detail::MatchAllOfGeneric<MatchersLHS..., MatcherRHS>{Detail::array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
template<typename MatcherLHS, typename... MatchersRHS>
typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatchersRHS...>>::type
operator && (MatcherLHS const& lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) {
return MatchAllOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
template<typename MatcherLHS, typename... MatchersRHS>
typename std::enable_if<Detail::is_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatchersRHS...>>::type
operator && (MatcherLHS const& lhs, Detail::MatchAllOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAllOfGeneric<MatcherLHS, MatchersRHS...>{Detail::array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename... MatchersRHS>
MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>
operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) {
return MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename... MatchersRHS>
Detail::MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>
operator || (Detail::MatchAnyOfGeneric<MatchersLHS...>&& lhs, Detail::MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>{Detail::array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename MatcherRHS>
typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>>::type
operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) {
return MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
template<typename... MatchersLHS, typename MatcherRHS>
typename std::enable_if<Detail::is_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>>::type
operator || (Detail::MatchAnyOfGeneric<MatchersLHS...>&& lhs, MatcherRHS const& rhs) {
return Detail::MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>{Detail::array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
template<typename MatcherLHS, typename... MatchersRHS>
typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>>::type
operator || (MatcherLHS const& lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) {
return MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
template<typename MatcherLHS, typename... MatchersRHS>
typename std::enable_if<Detail::is_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>>::type
operator || (MatcherLHS const& lhs, Detail::MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>{Detail::array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
template<typename MatcherT>
MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
return matcher.m_matcher;
}
template<typename MatcherT>
MatcherT const& operator ! (Detail::MatchNotOfGeneric<MatcherT> const& matcher) {
return matcher.m_matcher;
}
} // namespace Impl
} // namespace Matchers
using namespace Matchers;
using Matchers::Impl::MatcherGenericBase;
} // namespace Catch
#endif //TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED