From 94425ad59b3e9ac9e7eea151c5f7843b8047cc1e Mon Sep 17 00:00:00 2001 From: Sergey Semushin Date: Wed, 5 Apr 2017 10:53:10 +0300 Subject: [PATCH] Add opt-in c++11 stream insertable check. (#877) * Add opt-in c++11 stream insertable check. To opt-in, define CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK. Opt-in fixes #872 and should fix #757 as well. --- docs/configuration.md | 45 +++++++++++++++++++------- include/internal/catch_tostring.h | 15 +++++++++ projects/SelfTest/CompilationTests.cpp | 19 +++++++++++ 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3e0628ee..08a9733a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -52,17 +52,18 @@ This can be useful on certain platforms that do not provide ```std::cout``` and # C++ conformance toggles - CATCH_CONFIG_CPP11_NULLPTR // nullptr is supported? - CATCH_CONFIG_CPP11_NOEXCEPT // noexcept is supported? - CATCH_CONFIG_CPP11_GENERATED_METHODS // delete and default keywords for methods - CATCH_CONFIG_CPP11_IS_ENUM // std::is_enum is supported? - CATCH_CONFIG_CPP11_TUPLE // std::tuple is supported - CATCH_CONFIG_VARIADIC_MACROS // Usually pre-C++11 compiler extensions are sufficient - CATCH_CONFIG_CPP11_LONG_LONG // generates overloads for the long long type - CATCH_CONFIG_CPP11_OVERRIDE // CATCH_OVERRIDE expands to override (for virtual function implementations) - CATCH_CONFIG_CPP11_UNIQUE_PTR // Use std::unique_ptr instead of std::auto_ptr - CATCH_CONFIG_CPP11_SHUFFLE // Use std::shuffle instead of std::random_shuffle - CATCH_CONFIG_CPP11_TYPE_TRAITS // Use std::enable_if and + CATCH_CONFIG_CPP11_NULLPTR // nullptr is supported? + CATCH_CONFIG_CPP11_NOEXCEPT // noexcept is supported? + CATCH_CONFIG_CPP11_GENERATED_METHODS // delete and default keywords for methods + CATCH_CONFIG_CPP11_IS_ENUM // std::is_enum is supported? + CATCH_CONFIG_CPP11_TUPLE // std::tuple is supported + CATCH_CONFIG_VARIADIC_MACROS // Usually pre-C++11 compiler extensions are sufficient + CATCH_CONFIG_CPP11_LONG_LONG // generates overloads for the long long type + CATCH_CONFIG_CPP11_OVERRIDE // CATCH_OVERRIDE expands to override (for virtual function implementations) + CATCH_CONFIG_CPP11_UNIQUE_PTR // Use std::unique_ptr instead of std::auto_ptr + CATCH_CONFIG_CPP11_SHUFFLE // Use std::shuffle instead of std::random_shuffle + CATCH_CONFIG_CPP11_TYPE_TRAITS // Use std::enable_if and + CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK // Use C++11 expression SFINAE to check if class can be inserted to std::ostream Catch has some basic compiler detection that will attempt to select the appropriate mix of these macros. However being incomplete - and often without access to the respective compilers - this detection tends to be conservative. So overriding control is given to the user. If a compiler supports a feature (and Catch does not already detect it) then one or more of these may be defined to enable it (or suppress it, in some cases). If you do do this please raise an issue, specifying your compiler version (ideally with an idea of how to detect it) and stating that it has such support. @@ -70,6 +71,28 @@ You may also suppress any of these features by using the `_NO_` form, e.g. `CATC All C++11 support can be disabled with `CATCH_CONFIG_NO_CPP11` +## `CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK` + +This flag is off by default, but allows you to resolve problems caused by types with private base class that are streamable, but the classes themselves are not. Without it, the following code will cause a compilation error: +```cpp +#define CATCH_CONFIG_MAIN +#include +struct A {}; +std::ostream &operator<< (std::ostream &o, const A &v) { return o << 0; } + +struct B : private A { + bool operator==(int){ return true;} +}; + +B f (); +std::ostream g (); + +TEST_CASE ("Error in streamable check") { + B x; + REQUIRE (x == 4); +} +``` + # Other toggles CATCH_CONFIG_COUNTER // Use __COUNTER__ to generate unique names for test cases diff --git a/include/internal/catch_tostring.h b/include/internal/catch_tostring.h index e6f7ec9d..21ab99a0 100644 --- a/include/internal/catch_tostring.h +++ b/include/internal/catch_tostring.h @@ -72,6 +72,7 @@ namespace Detail { extern const std::string unprintableString; + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; @@ -90,6 +91,20 @@ namespace Detail { static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; +#else + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; +#endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template(3)); } +// Test containing example where original stream insertable check breaks compilation +#if defined (CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) +namespace { + struct A {}; + std::ostream& operator<< (std::ostream &o, const A &) { return o << 0; } + + struct B : private A { + bool operator== (int) const { return true; } + }; + + B f (); + std::ostream g (); +} + +TEST_CASE( "#872" ) { + B x; + REQUIRE (x == 4); +} +#endif