Skip to content

Commit

Permalink
CRTP matchers, Closes catchorg#1553
Browse files Browse the repository at this point in the history
  • Loading branch information
Dvir Yitzchaki committed Feb 9, 2020
1 parent 0b2874b commit 3bc47ea
Show file tree
Hide file tree
Showing 28 changed files with 354 additions and 285 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -28,3 +28,6 @@ Build
cmake-build-*
benchmark-dir
.conan/test_package/build
_*/
.vscode/
.history/
10 changes: 5 additions & 5 deletions docs/matchers.md
Expand Up @@ -147,31 +147,31 @@ REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("De
It's easy to provide your own matchers to extend Catch or just to work with your own types.
You need to provide two things:
1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the matcher class.
The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
override two methods: `match()` and `describe()`.
provide two methods: `match()` and `describe()`.
2. A simple builder function. This is what is actually called from the test code and allows overloading.
Here's an example for asserting that an integer falls within a given range
(note that it is all inline for the sake of keeping the example short):
```c++
// The matcher class
class IntRange : public Catch::MatcherBase<int> {
class IntRange : public Catch::MatcherBase<IntRange> {
int m_begin, m_end;
public:
IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}
// Performs the test for this matcher
bool match( int const& i ) const override {
bool match( int const& i ) const {
return i >= m_begin && i <= m_end;
}
// Produces a string describing what this matcher does. It should
// include any provided data (the begin/ end in this case) and
// be written as if it were stating a fact (in the output it will be
// preceded by the value under test).
virtual std::string describe() const override {
std::string describe() const {
std::ostringstream ss;
ss << "is between " << m_begin << " and " << m_end;
return ss.str();
Expand Down
2 changes: 0 additions & 2 deletions src/CMakeLists.txt
Expand Up @@ -129,7 +129,6 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_approx.cpp
${SOURCES_DIR}/catch_assertionhandler.cpp
${SOURCES_DIR}/catch_assertionresult.cpp
${SOURCES_DIR}/catch_capture_matchers.cpp
${SOURCES_DIR}/catch_commandline.cpp
${SOURCES_DIR}/catch_common.cpp
${SOURCES_DIR}/catch_config.cpp
Expand All @@ -153,7 +152,6 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_interfaces_testcase.cpp
${SOURCES_DIR}/catch_list.cpp
${SOURCES_DIR}/catch_leak_detector.cpp
${SOURCES_DIR}/catch_matchers.cpp
${SOURCES_DIR}/catch_matchers_exception.cpp
${SOURCES_DIR}/catch_matchers_floating.cpp
${SOURCES_DIR}/catch_matchers_generic.cpp
Expand Down
24 changes: 0 additions & 24 deletions src/catch2/catch_capture_matchers.cpp

This file was deleted.

24 changes: 16 additions & 8 deletions src/catch2/catch_capture_matchers.h
Expand Up @@ -11,18 +11,19 @@
#include <catch2/catch_capture.hpp>
#include <catch2/catch_matchers.h>
#include <catch2/catch_stringref.h>
#include <catch2/catch_interfaces_registry_hub.h>

namespace Catch {

template<typename ArgT, typename MatcherT>
class MatchExpr : public ITransientExpression {
ArgT const& m_arg;
ArgT m_arg;
MatcherT m_matcher;
StringRef m_matcherString;
public:
MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
MatchExpr( ArgT arg, MatcherT const& matcher, StringRef const& matcherString )
: ITransientExpression{ true, matcher.match( arg ) },
m_arg( arg ),
m_arg( std::forward<ArgT>(arg) ),
m_matcher( matcher ),
m_matcherString( matcherString )
{}
Expand All @@ -37,13 +38,20 @@ namespace Catch {
}
};

using StringMatcher = Matchers::Impl::MatcherBase<std::string>;

void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString );
// 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)
template<typename StringMatcher,
typename = typename std::enable_if<std::is_base_of<MatcherBase<StringMatcher>, StringMatcher>::value>::type>
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) {
std::string exceptionMessage = Catch::translateActiveException();
MatchExpr<std::string const&, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
handler.handleExpr( expr );
}

template<typename ArgT, typename MatcherT>
auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> {
return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT&&, MatcherT> {
return MatchExpr<ArgT&&, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString );
}

} // namespace Catch
Expand Down
17 changes: 17 additions & 0 deletions src/catch2/catch_common.h
Expand Up @@ -80,6 +80,23 @@ namespace Catch {
T const& operator + ( T const& value, StreamEndStop ) {
return value;
}

// backported index_sequnce
template <std::size_t ...>
struct indexSequence
{ };

template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };

template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };

template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

}

#define CATCH_INTERNAL_LINEINFO \
Expand Down
28 changes: 0 additions & 28 deletions src/catch2/catch_matchers.cpp

This file was deleted.

0 comments on commit 3bc47ea

Please sign in to comment.