diff options
| author | Martin Hořeňovský | 2018-01-10 13:53:04 +0100 |
|---|---|---|
| committer | Martin Hořeňovský | 2018-01-10 13:53:04 +0100 |
| commit | cd76f5730c9a3afa19f3b9c83608d9c7ab325a19 (patch) | |
| tree | b189f1e5399737491266204879bbd185669f799a | |
| parent | f5910f38ef38ac2cca9f3830804abf6d9a8d40bf (diff) | |
| download | catch3-2.1.0.tar.gz | |
v2.1.0v2.1.0
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | conanfile.py | 2 | ||||
| -rw-r--r-- | docs/release-notes.md | 41 | ||||
| -rw-r--r-- | include/internal/catch_version.cpp | 2 | ||||
| -rw-r--r-- | single_include/catch.hpp | 5208 | ||||
| -rw-r--r-- | single_include/catch_reporter_automake.hpp | 62 | ||||
| -rw-r--r-- | single_include/catch_reporter_tap.hpp | 255 | ||||
| -rw-r--r-- | single_include/catch_reporter_teamcity.hpp | 220 | ||||
| -rw-r--r-- | test_package/conanfile.py | 2 |
10 files changed, 3549 insertions, 2249 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a11485..0fa91501 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest) set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark) set(HEADER_DIR ${CATCH_DIR}/include) -set(CATCH_VERSION_NUMBER 2.0.1) +set(CATCH_VERSION_NUMBER 2.1.0) if(USE_WMAIN) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup") @@ -5,13 +5,13 @@ [](https://travis-ci.org/catchorg/Catch2) [](https://ci.appveyor.com/project/catchorg/catch2) [](https://codecov.io/gh/catchorg/Catch2) -[](https://wandbox.org/permlink/tB8z0G3kMAIZkIca) +[](https://wandbox.org/permlink/nerce2MiN6sVmfCi) <a href="https://github.com/catchorg/Catch2/releases/download/v2.0.1/catch.hpp">The latest version of the single header can be downloaded directly using this link</a> ## Catch2 is released! -If you've been using an earlier version of Catch, please see the +If you've been using an earlier version of Catch, please see the Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1) before moving to Catch2. You might also like to read [this blog post](http://www.levelofindirection.com/journal/2017/11/3/catch2-released.html) for more details. diff --git a/conanfile.py b/conanfile.py index b4993b71..354f68a4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -4,7 +4,7 @@ from conans import ConanFile class CatchConan(ConanFile): name = "Catch" - version = "2.0.1" + version = "2.1.0" description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD" author = "philsquared" generators = "cmake" diff --git a/docs/release-notes.md b/docs/release-notes.md index 1a762dbd..5c02f6bf 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,4 +1,45 @@ <a id="top"></a> + +# 2.1.0 + +## Improvements +* Various performance improvements + * On top of the performance regression fixes +* Experimental support for PCH was added (#1061) +* `CATCH_CONFIG_EXTERNAL_INTERFACES` now brings in declarations of Console, Compact, XML and JUnit reporters +* `MatcherBase` no longer has a pointless second template argument +* Reduced the number of warning suppressions that leak into user's code + * Bugs in g++ 4.x and 5.x mean that some of them have to be left in + + +## Fixes +* Fixed performance regression from Catch classic + * One of the performance improvement patches for Catch classic was not applied to Catch2 +* Fixed platform detection for iOS (#1084) +* Fixed compilation when `g++` is used together with `libc++` (#1110) +* Fixed TeamCity reporter compilation with the single header version + * To fix the underlying issue we will be versioning reporters in single_include folder per release +* The XML reporter will now report `WARN` messages even when not used with `-s` +* Fixed compilation when `VectorContains` matcher was combined using `&&` (#1092) +* Fixed test duration overflowing after 10 seconds (#1125, #1129) +* Fixed `std::uncaught_exception` deprecation warning (#1124) + + +## New features +* New Matchers + * Regex matcher for strings, `Matches`. + * Set-equal matcher for vectors, `UnorderedEquals` + * Floating point matchers, `WithinAbs` and `WithinULP`. +* Stringification now attempts to decompose all containers (#606) + * Containers are objects that respond to ADL `begin(T)` and `end(T)`. + + +## Other changes +* Reporters will now be versioned in the `single_include` folder to ensure their compatibility with the last released version + + + + # 2.0.1 ## Breaking changes diff --git a/include/internal/catch_version.cpp b/include/internal/catch_version.cpp index a9dc64e6..8b50ace0 100644 --- a/include/internal/catch_version.cpp +++ b/include/internal/catch_version.cpp @@ -37,7 +37,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 0, 1, "", 0 ); + static Version version( 2, 1, 0, "", 0 ); return version; } diff --git a/single_include/catch.hpp b/single_include/catch.hpp index 362f8693..ef720908 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.0.1 - * Generated: 2017-11-03 11:53:39.642003 + * Catch v2.1.0 + * Generated: 2018-01-10 13:51:15.378034 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -26,9 +26,6 @@ # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" @@ -36,27 +33,33 @@ # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wparentheses" - # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h #ifdef __APPLE__ # include <TargetConditionals.h> -# if TARGET_OS_MAC == 1 +# if TARGET_OS_OSX == 1 # define CATCH_PLATFORM_MAC # elif TARGET_OS_IPHONE == 1 # define CATCH_PLATFORM_IPHONE @@ -70,6 +73,7 @@ #endif // end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -77,6 +81,13 @@ # endif #endif +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h // start catch_tag_alias_autoregistrar.h // start catch_common.h @@ -228,7 +239,10 @@ namespace Catch { struct SourceLineInfo { SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo && ) = default; @@ -245,11 +259,6 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - bool isTrue( bool value ); - bool alwaysTrue(); - bool alwaysFalse(); - // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as @@ -275,7 +284,10 @@ namespace Catch { } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -330,10 +342,12 @@ namespace Catch { /// visible - but it does mean (substring) StringRefs should not be shared between /// threads. class StringRef { - friend struct StringRefTestAccess; - + public: using size_type = std::size_t; + private: + friend struct StringRefTestAccess; + char const* m_start; size_type m_size; @@ -341,16 +355,50 @@ namespace Catch { void takeOwnership(); + static constexpr char const* const s_empty = ""; + public: // construction/ assignment - StringRef() noexcept; - StringRef( StringRef const& other ) noexcept; - StringRef( StringRef&& other ) noexcept; + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + StringRef( char const* rawChars ) noexcept; - StringRef( char const* rawChars, size_type size ) noexcept; - StringRef( std::string const& stdString ) noexcept; - ~StringRef() noexcept; - auto operator = ( StringRef other ) noexcept -> StringRef&; + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + operator std::string() const; void swap( StringRef& other ) noexcept; @@ -362,8 +410,13 @@ namespace Catch { auto operator[] ( size_type index ) const noexcept -> char; public: // named queries - auto empty() const noexcept -> bool; - auto size() const noexcept -> size_type; + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + auto numberOfCharacters() const noexcept -> size_type; auto c_str() const -> char const*; @@ -382,6 +435,10 @@ namespace Catch { auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + } // namespace Catch // end catch_stringref.h @@ -407,7 +464,7 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { } struct NameAndTags { - NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; + NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept; StringRef name; StringRef tags; }; @@ -473,15 +530,121 @@ struct AutoReg : NonCopyable { // start catch_assertionhandler.h +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h // start catch_decomposer.h // start catch_tostring.h -#include <sstream> #include <vector> #include <cstddef> #include <type_traits> #include <string> +// start catch_stream.h + +#include <iosfwd> +#include <cstddef> +#include <ostream> + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template<typename T> + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + + static void cleanup(); + }; +} + +// end catch_stream.h #ifdef __OBJC__ // start catch_objc_arc.hpp @@ -535,7 +698,7 @@ inline id performOptionalSelector( id obj, SEL sel ) { #endif // We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy; +struct Catch_global_namespace_dummy {}; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { @@ -566,25 +729,37 @@ namespace Catch { static const bool value = decltype(test<std::ostream, const T&>(0))::value; }; + template<typename E> + std::string convertUnknownEnumToString( E e ); + + template<typename T> + typename std::enable_if<!std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + }; + template<typename T> + typename std::enable_if<std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + }; + } // namespace Detail // If we decide for C++14, change these to enable_if_ts - template <typename T> + template <typename T, typename = void> struct StringMaker { template <typename Fake = T> static typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type - convert(const Fake& t) { - std::ostringstream sstr; - sstr << t; - return sstr.str(); + convert(const Fake& value) { + ReusableStringStream rss; + rss << value; + return rss.str(); } template <typename Fake = T> static typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type - convert(const Fake&) { - return Detail::unprintableString; + convert( const Fake& value ) { + return Detail::convertUnstreamable( value ); } }; @@ -597,6 +772,11 @@ namespace Catch { return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); } + template<typename E> + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); + } + } // namespace Detail // Some predefined specializations @@ -627,6 +807,18 @@ namespace Catch { static std::string convert(wchar_t * str); }; + template<typename T> + struct is_string_array : std::false_type {}; + + template<std::size_t N> + struct is_string_array<char[N]> : std::true_type {}; + + template<std::size_t N> + struct is_string_array<signed char[N]> : std::true_type {}; + + template<std::size_t N> + struct is_string_array<unsigned char[N]> : std::true_type {}; + template<int SZ> struct StringMaker<char[SZ]> { static std::string convert(const char* str) { @@ -729,32 +921,18 @@ namespace Catch { namespace Detail { template<typename InputIterator> std::string rangeToString(InputIterator first, InputIterator last) { - std::ostringstream oss; - oss << "{ "; + ReusableStringStream rss; + rss << "{ "; if (first != last) { - oss << ::Catch::Detail::stringify(*first); + rss << ::Catch::Detail::stringify(*first); for (++first; first != last; ++first) - oss << ", " << ::Catch::Detail::stringify(*first); + rss << ", " << ::Catch::Detail::stringify(*first); } - oss << " }"; - return oss.str(); + rss << " }"; + return rss.str(); } } - template<typename T, typename Allocator> - struct StringMaker<std::vector<T, Allocator> > { - static std::string convert( std::vector<T,Allocator> const& v ) { - return ::Catch::Detail::rangeToString( v.begin(), v.end() ); - } - }; - - template<typename T> - struct EnumStringMaker { - static std::string convert(const T& t) { - return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t)); - } - }; - #ifdef __OBJC__ template<> struct StringMaker<NSString*> { @@ -798,13 +976,13 @@ namespace Catch { template<typename T1, typename T2> struct StringMaker<std::pair<T1, T2> > { static std::string convert(const std::pair<T1, T2>& pair) { - std::ostringstream oss; - oss << "{ " + ReusableStringStream rss; + rss << "{ " << ::Catch::Detail::stringify(pair.first) << ", " << ::Catch::Detail::stringify(pair.second) << " }"; - return oss.str(); + return rss.str(); } }; } @@ -841,22 +1019,72 @@ namespace Catch { template<typename ...Types> struct StringMaker<std::tuple<Types...>> { static std::string convert(const std::tuple<Types...>& tuple) { - std::ostringstream os; - os << '{'; - Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, os); - os << " }"; - return os.str(); + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); } }; } #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template <typename T> + struct is_range { + static const bool value = + !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value && + !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; + }; + + template<typename Range> + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector<bool> specially + template<typename Allocator> + std::string rangeToString( std::vector<bool, Allocator> const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template<typename R> + struct StringMaker<R, typename std::enable_if<is_range<R>::value && !is_string_array<R>::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + +} // namespace Catch + // Separate std::chrono::duration specialization #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) #include <ctime> #include <ratio> #include <chrono> +namespace Catch { + template <class Ratio> struct ratio_string { static std::string symbol(); @@ -864,69 +1092,68 @@ struct ratio_string { template <class Ratio> std::string ratio_string<Ratio>::symbol() { - std::ostringstream oss; - oss << '[' << Ratio::num << '/' + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' << Ratio::den << ']'; - return oss.str(); + return rss.str(); } template <> struct ratio_string<std::atto> { - static std::string symbol() { return "a"; } + static std::string symbol(); }; template <> struct ratio_string<std::femto> { - static std::string symbol() { return "f"; } + static std::string symbol(); }; template <> struct ratio_string<std::pico> { - static std::string symbol() { return "p"; } + static std::string symbol(); }; template <> struct ratio_string<std::nano> { - static std::string symbol() { return "n"; } + static std::string symbol(); }; template <> struct ratio_string<std::micro> { - static std::string symbol() { return "u"; } + static std::string symbol(); }; template <> struct ratio_string<std::milli> { - static std::string symbol() { return "m"; } + static std::string symbol(); }; -namespace Catch { //////////// // std::chrono::duration specializations template<typename Value, typename Ratio> struct StringMaker<std::chrono::duration<Value, Ratio>> { static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { - std::ostringstream oss; - oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; - return oss.str(); + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; + return rss.str(); } }; template<typename Value> struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> { static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { - std::ostringstream oss; - oss << duration.count() << " s"; - return oss.str(); + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); } }; template<typename Value> struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> { static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { - std::ostringstream oss; - oss << duration.count() << " m"; - return oss.str(); + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); } }; template<typename Value> struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> { static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { - std::ostringstream oss; - oss << duration.count() << " h"; - return oss.str(); + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); } }; @@ -972,7 +1199,7 @@ namespace Catch { #endif // end catch_tostring.h -#include <ostream> +#include <iosfwd> #ifdef _MSC_VER #pragma warning(push) @@ -985,27 +1212,32 @@ namespace Catch { namespace Catch { struct ITransientExpression { - virtual auto isBinaryExpression() const -> bool = 0; - virtual auto getResult() const -> bool = 0; + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } virtual void streamReconstructedExpression( std::ostream &os ) const = 0; - // We don't actually need a virtual destructore, but many static analysers + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers // complain if it's not here :-( virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + }; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template<typename LhsT, typename RhsT> class BinaryExpr : public ITransientExpression { - bool m_result; LhsT m_lhs; StringRef m_op; RhsT m_rhs; - auto isBinaryExpression() const -> bool override { return true; } - auto getResult() const -> bool override { return m_result; } - void streamReconstructedExpression( std::ostream &os ) const override { formatReconstructedExpression ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); @@ -1013,7 +1245,7 @@ namespace Catch { public: BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : m_result( comparisonResult ), + : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) @@ -1024,15 +1256,15 @@ namespace Catch { class UnaryExpr : public ITransientExpression { LhsT m_lhs; - auto isBinaryExpression() const -> bool override { return false; } - auto getResult() const -> bool override { return m_lhs ? true : false; } - void streamReconstructedExpression( std::ostream &os ) const override { os << Catch::Detail::stringify( m_lhs ); } public: - UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, lhs ? true : false }, + m_lhs( lhs ) + {} }; // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) @@ -1062,43 +1294,43 @@ namespace Catch { class ExprLhs { LhsT m_lhs; public: - ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} template<typename RhsT> auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; } auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { - return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs ); + return { m_lhs == rhs, m_lhs, "==", rhs }; } template<typename RhsT> auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; } auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { - return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs ); + return { m_lhs != rhs, m_lhs, "!=", rhs }; } template<typename RhsT> auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( m_lhs > rhs, m_lhs, ">", rhs ); + return { m_lhs > rhs, m_lhs, ">", rhs }; } template<typename RhsT> auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( m_lhs < rhs, m_lhs, "<", rhs ); + return { m_lhs < rhs, m_lhs, "<", rhs }; } template<typename RhsT> auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( m_lhs >= rhs, m_lhs, ">=", rhs ); + return { m_lhs >= rhs, m_lhs, ">=", rhs }; } template<typename RhsT> auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { - return BinaryExpr<LhsT, RhsT const&>( m_lhs <= rhs, m_lhs, "<=", rhs ); + return { m_lhs <= rhs, m_lhs, "<=", rhs }; } auto makeUnaryExpr() const -> UnaryExpr<LhsT> { - return UnaryExpr<LhsT>( m_lhs ); + return UnaryExpr<LhsT>{ |