From 5062d3e92bf3eb9a58b84083f1b7062fe7826591 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 16 Apr 2013 22:55:31 +0100 Subject: [PATCH] Added (conditional) SFINAE support. Better streamable detection for toString using SFINAE (falls back to non-SFINAE version without) --- .../internal/catch_compiler_capabilities.h | 74 +++++++++++++++++++ include/internal/catch_sfinae.hpp | 44 +++++++++++ include/internal/catch_tostring.hpp | 52 ++++++++++++- projects/SelfTest/TrickyTests.cpp | 16 ++-- .../CatchSelfTest.xcodeproj/project.pbxproj | 4 + 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 include/internal/catch_compiler_capabilities.h create mode 100644 include/internal/catch_sfinae.hpp diff --git a/include/internal/catch_compiler_capabilities.h b/include/internal/catch_compiler_capabilities.h new file mode 100644 index 00000000..f3e792d1 --- /dev/null +++ b/include/internal/catch_compiler_capabilities.h @@ -0,0 +1,74 @@ +/* + * Created by Phil on 15/04/2013. + * Copyright 2013 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) + */ +#ifndef TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Much of the following code is based on Boost (1.53) + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#if (__BORLANDC__ > 0x582 ) +#define CATCH_SFINAE +#endif + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#if (__EDG_VERSION__ > 238 ) +#define CATCH_SFINAE +#endif + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#if (__DMC__ > 0x840 ) +#define CATCH_SFINAE +#endif + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ < 3 + +#if (__GNUC_MINOR__ >= 96 ) +#define CATCH_SFINAE +#endif + +#elif __GNUC__ >= 3 + +#define CATCH_SFINAE + +#endif // __GNUC__ < 3 + + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1310 ) // (VC++ 7.0+) +#define CATCH_SFINAE +#endif + +#endif // _MSC_VER + + +#endif // TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + diff --git a/include/internal/catch_sfinae.hpp b/include/internal/catch_sfinae.hpp new file mode 100644 index 00000000..ef414f84 --- /dev/null +++ b/include/internal/catch_sfinae.hpp @@ -0,0 +1,44 @@ +/* + * Created by Phil on 15/04/2013. + * Copyright 2013 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) + */ +#ifndef TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED + +// Try to detect if the current compiler supports SFINAE +#include "catch_compiler_capabilities.h" + +#ifdef CATCH_SFINAE + +namespace Catch { + + struct TrueType { + static const bool value = true; + typedef void Enable; + char sizer[1]; + }; + struct FalseType { + static const bool value = false; + typedef void Disable; + char sizer[2]; + }; + + template struct NotABooleanExpression; + + template struct If : NotABooleanExpression {}; + template<> struct If : TrueType {}; + template<> struct If : FalseType {}; + + template struct SizedIf; + template<> struct SizedIf : TrueType {}; + template<> struct SizedIf : FalseType {}; + +} // end namespace Catch + +#endif // CATCH_SFINAE + +#endif // TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED + diff --git a/include/internal/catch_tostring.hpp b/include/internal/catch_tostring.hpp index f87127e0..0f7b8380 100644 --- a/include/internal/catch_tostring.hpp +++ b/include/internal/catch_tostring.hpp @@ -9,6 +9,8 @@ #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED #include "catch_common.h" +#include "catch_sfinae.hpp" + #include #include #include @@ -18,6 +20,49 @@ #endif namespace Catch { + +#ifdef CATCH_SFINAE + +namespace Detail { + + template + class IsStreamInsertableHelper { + template struct TrueIfSizeable : TrueType {}; + + template + static TrueIfSizeable dummy(T2*); + static FalseType dummy(...); + + public: + typedef SizedIf type; + }; + + template + struct IsStreamInsertable : IsStreamInsertableHelper::type {}; + + template + void toStream( std::ostream& os, T const& value, typename IsStreamInsertable::Enable* = 0 ) { + os << value; + } + + template + void toStream( std::ostream& os, T const&, typename IsStreamInsertable::Disable* = 0 ) { + os << "{?}"; + } + +} + +template +struct StringMaker { + static std::string convert( T const& value ) { + std::ostringstream oss; + Detail::toStream( oss, value ); + return oss.str(); + } +}; + +#else + namespace Detail { struct NonStreamable { @@ -40,6 +85,10 @@ struct StringMaker { return oss.str(); } }; + +#endif + + template struct StringMaker { static std::string convert( T const* p ) { @@ -57,7 +106,7 @@ struct StringMaker > { std::ostringstream oss; oss << "{ "; for( std::size_t i = 0; i < v.size(); ++ i ) { - oss << v[i]; + oss << toString( v[i] ); if( i < v.size() - 1 ) oss << ", "; } @@ -83,7 +132,6 @@ namespace Detail { template std::string toString( const T& value ) { return StringMaker::convert( value ); -// return Detail::makeString( value ); } // Built in overloads diff --git a/projects/SelfTest/TrickyTests.cpp b/projects/SelfTest/TrickyTests.cpp index 2326ca05..a40cecd5 100644 --- a/projects/SelfTest/TrickyTests.cpp +++ b/projects/SelfTest/TrickyTests.cpp @@ -182,7 +182,7 @@ namespace ObjectWithConversions "Operators at different namespace levels not hijacked by Koenig lookup" ) { - Object o; + Object o; REQUIRE(0xc0000000 == o ); } } @@ -337,14 +337,14 @@ TEST_CASE( "Assertions then sections", "" ) } } -class Awkward +struct Awkward { operator int() const { return 7; } }; -//TEST_CASE( "non streamable", "" ) -//{ -// Awkward awkward; -// std::string s = Catch::toString( awkward ); -// REQUIRE( s == "{?}" ); -//} +TEST_CASE( "non streamable", "" ) +{ + Awkward awkward; + std::string s = Catch::toString( awkward ); + REQUIRE( s == "7" ); // This is ambiguous without SFINAE +} diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index bef07f61..d54036ab 100644 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -59,6 +59,8 @@ 266ECD73170F3C620030D735 /* BDDTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BDDTests.cpp; path = ../../../SelfTest/BDDTests.cpp; sourceTree = ""; }; 266ECD8C1713614B0030D735 /* catch_legacy_reporter_adapter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_legacy_reporter_adapter.hpp; sourceTree = ""; }; 266ECD8D1713614B0030D735 /* catch_legacy_reporter_adapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_legacy_reporter_adapter.h; sourceTree = ""; }; + 26759472171C72A400A84BD1 /* catch_sfinae.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_sfinae.hpp; sourceTree = ""; }; + 26759473171C74C200A84BD1 /* catch_compiler_capabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_compiler_capabilities.h; sourceTree = ""; }; 26847E5B16BBAB790043B9C1 /* catch_message.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_message.h; sourceTree = ""; }; 26847E5C16BBACB60043B9C1 /* catch_message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_message.hpp; sourceTree = ""; }; 26847E5D16BBADB40043B9C1 /* catch_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_message.cpp; path = ../../../SelfTest/SurrogateCpps/catch_message.cpp; sourceTree = ""; }; @@ -395,6 +397,8 @@ 4AEE0326161431070071E950 /* catch_streambuf.h */, 4ACE21C8166CA19700FB5509 /* catch_option.hpp */, 2694A1F8169FFF9B004816E3 /* catch_line_wrap.h */, + 26759472171C72A400A84BD1 /* catch_sfinae.hpp */, + 26759473171C74C200A84BD1 /* catch_compiler_capabilities.h */, ); name = Infrastructure; sourceTree = "";