/* * Created by Phil on 8/5/2012. * Copyright 2012 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_TOSTRING_H_INCLUDED #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #include "catch_common.h" #include #include #include #include #include #ifndef CATCH_CPP11_OR_GREATER #include #endif #ifdef __OBJC__ #include "catch_objc_arc.hpp" #endif #ifdef CATCH_CONFIG_CPP11_TUPLE #include #endif #ifdef CATCH_CPP11_OR_GREATER #include #endif namespace Catch { // Why we're here. template std::string toString( T const& value ); // Built in overloads std::string toString( std::string const& value ); std::string toString( std::wstring const& value ); std::string toString( const char* const value ); std::string toString( char* const value ); std::string toString( const wchar_t* const value ); std::string toString( wchar_t* const value ); std::string toString( int value ); std::string toString( unsigned long value ); std::string toString( unsigned int value ); std::string toString( const double value ); std::string toString( const float value ); std::string toString( bool value ); std::string toString( char value ); std::string toString( signed char value ); std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ); std::string toString( unsigned long long value ); #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); std::string toString( NSObject* const& nsObject ); #endif namespace Detail { extern const std::string unprintableString; struct BorgType { template BorgType( T const& ); }; // Without c++11's `decltype`, rely on function overloading returning // different sized types that can be discriminated with `sizeof`. struct TrueType { char sizer[1]; }; struct FalseType { char sizer[2]; }; TrueType& testStreamable( std::ostream& ); FalseType testStreamable( FalseType ); FalseType operator<<( std::ostream const&, BorgType const& ); template struct IsStreamInsertable { static std::ostream &s; static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template::value > struct EnumStringMaker { static std::string convert( T const& ) { return unprintableString; } }; template struct EnumStringMaker { static std::string convert( T const& v ) { return ::Catch::toString( static_cast::type>(v) ); } }; #endif template struct StringMakerBase { #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template static std::string convert( T const& v ) { return EnumStringMaker::convert( v ); } #else template static std::string convert( T const& ) { return unprintableString; } #endif }; template<> struct StringMakerBase { template static std::string convert( T const& _value ) { std::ostringstream oss; oss << _value; return oss.str(); } }; std::string rawMemoryToString( const void *object, std::size_t size ); template inline std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } } // end namespace Detail template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } #ifndef CATCH_CPP11_OR_GREATER template std::string toString( std::vector const& v ) { return Detail::rangeToString( v.begin(), v.end() ); } #else namespace Detail { // Importing c++11 std instead of prefixing call by std:: to allow for user // custom overload find through ADL using std::begin; using std::end; template struct HasBasicContainerApi { /// This overload will be chosen if all the operations in it's signature /// can be resolved at compile time. template()), // Test that begin and end return comparable values class = decltype(begin(std::declval()) != end(std::declval()))> static std::true_type has( typename C::const_iterator*, typename C::value_type*); /// Lower priority overload will be picked if the previous one fails. template static std::false_type has( ... ); static const bool value = decltype(has(NULL, NULL))::value; }; // Compile type function that returns true if a type matches // the container concept (http://en.cppreference.com/w/cpp/concept/Container), // false otherwise. template ::value> struct IsContainer { // C array are containers although not having the standard api. static const bool value = std::is_array::value; }; // Specialization for types that have a basic container API // that can now be checked for type inconsistency. template struct IsContainer { private: typedef typename C::value_type value_type; typedef typename C::const_iterator const_iterator; static const C& c; public: static const bool value = std::is_same::value && std::is_same::value && std::is_same::value && std::is_same::value; }; /// Print containers by printing their elements in succession template::value>::type, class = void> std::string toStringInternal( C const& c ) { return Detail::rangeToString( begin(c), end(c) ); } } // end namespace Detail #endif // CATCH_CPP11_OR_GREATER // toString for pairs template struct StringMaker > { static std::string convert( const std::pair& pair ) { std::ostringstream oss; oss << "{ " << toString( pair.first ) << ", " << toString( pair.second ) << " }"; return oss.str(); } }; #ifdef CATCH_CONFIG_CPP11_TUPLE // toString for tuples namespace TupleDetail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct ElementPrinter { static void print( const Tuple& tuple, std::ostream& os ) { os << ( N ? ", " : " " ) << Catch::toString(std::get(tuple)); ElementPrinter::print(tuple,os); } }; template< typename Tuple, std::size_t N > struct ElementPrinter { static void print( const Tuple&, std::ostream& ) {} }; } template struct StringMaker> { static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } }; #endif // CATCH_CONFIG_CPP11_TUPLE namespace Detail { template std::string makeString( T const& value ) { return StringMaker::convert( value ); } /// Instead of adding complex template overloading of public toString method, /// use an internal dispatcher which template can get as complicated as needed /// without impacting the public api. template::value>::type #endif > std::string toStringInternal( T const& value ) { return StringMaker::convert( value ); } } // end namespace Detail /// \brief converts any type to a string /// /// The default template forwards on to ostringstream - except when an /// ostringstream overload does not exist - in which case it attempts to detect /// that and writes {?}. /// Overload (not specialize) this template for custom types that you don't want /// to provide an ostream overload for. template std::string toString( T const& value ) { return Detail::toStringInternal(value); } namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ) { std::ostringstream oss; oss << "{ "; if( first != last ) { oss << Catch::toString( *first ); while( ++first != last ) oss << ", " << Catch::toString( *first ); } oss << " }"; return oss.str(); } } } // end namespace Catch #endif // TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED