/* * 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_HPP_INCLUDED #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED #include "catch_common.h" #include "catch_sfinae.hpp" #include "catch_interfaces_config.h" #include #include #include #include #ifdef __OBJC__ #include "catch_objc_arc.hpp" #endif namespace Catch { namespace Detail { // SFINAE is currently disabled by default for all compilers. // If the non SFINAE version of IsStreamInsertable is ambiguous for you // and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE #ifdef CATCH_CONFIG_SFINAE template class IsStreamInsertableHelper { template struct TrueIfSizeable : TrueType {}; template static TrueIfSizeable dummy(T2*); static FalseType dummy(...); public: typedef SizedIf type; }; template struct IsStreamInsertable : IsStreamInsertableHelper::type {}; #else struct BorgType { template BorgType( T const& ); }; 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 ) }; }; #endif template struct StringMakerBase { template static std::string convert( T const& ) { return "{?}"; } }; template<> struct StringMakerBase { template static std::string convert( T const& _value ) { std::ostringstream oss; oss << _value; return oss.str(); } }; struct Endianness { enum Arch { Big, Little }; static Arch which() { union { int asInt; char asChar[sizeof (int)]; }; asInt = 1; return ( asChar[sizeof(int)-1] == 1 ) ? Big : Little; } }; // Writes the raw memory into a string, considering endianness template std::string rawMemoryToString( T value ) { union { T typedValue; unsigned char bytes[sizeof(T)]; }; typedValue = value; std::ostringstream oss; oss << "0x"; int i = 0, end = sizeof(T), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } for( ; i != end; i += inc ) oss << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)bytes[i]; return oss.str(); } } // end namespace Detail template std::string toString( T const& value ); template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } template struct StringMaker > { static std::string convert( std::vector const& v ) { return Detail::rangeToString( v.begin(), v.end() ); } }; namespace Detail { template inline std::string makeString( 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 specialise) this template for custom typs that you don't want /// to provide an ostream overload for. template std::string toString( T const& value ) { return StringMaker::convert( value ); } // Built in overloads ///////// !TBD: move this into impl file inline std::string toString( std::string const& value ) { std::string s = value; if( getCurrentContext().getConfig()->showInvisibles() ) { for(size_t i = 0; i < s.size(); ++i ) { std::string subs; switch( s[i] ) { case '\n': subs = "\\n"; break; case '\t': subs = "\\t"; break; default: break; } if( !subs.empty() ) { s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++i; } } } return "\"" + s + "\""; } inline std::string toString( std::wstring const& value ) { std::string s; s.reserve( value.size() ); for(size_t i = 0; i < value.size(); ++i ) s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; return toString( s ); } inline std::string toString( const char* const value ) { return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); } inline std::string toString( char* const value ) { return Catch::toString( static_cast( value ) ); } inline std::string toString( int value ) { std::ostringstream oss; oss << value; return oss.str(); } inline std::string toString( unsigned long value ) { std::ostringstream oss; if( value > 8192 ) oss << "0x" << std::hex << value; else oss << value; return oss.str(); } inline std::string toString( unsigned int value ) { return toString( static_cast( value ) ); } inline std::string toString( const double value ) { std::ostringstream oss; oss << std::setprecision( 10 ) << std::fixed << value; std::string d = oss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) i++; d = d.substr( 0, i+1 ); } return d; } inline std::string toString( bool value ) { return value ? "true" : "false"; } inline std::string toString( char value ) { return value < ' ' ? toString( static_cast( value ) ) : Detail::makeString( value ); } inline std::string toString( signed char value ) { return toString( static_cast( value ) ); } inline std::string toString( unsigned char value ) { return toString( static_cast( value ) ); } #ifdef CATCH_CONFIG_CPP11_NULLPTR inline std::string toString( std::nullptr_t ) { return "nullptr"; } #endif #ifdef __OBJC__ inline std::string toString( NSString const * const& nsstring ) { if( !nsstring ) return "nil"; return std::string( "@\"" ) + [nsstring UTF8String] + "\""; } inline std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { if( !nsstring ) return "nil"; return std::string( "@\"" ) + [nsstring UTF8String] + "\""; } inline std::string toString( NSObject* const& nsObject ) { return toString( [nsObject description] ); } #endif namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ) { std::ostringstream oss; oss << "{ "; if( first != last ) { oss << toString( *first ); for( ++first ; first != last ; ++first ) { oss << ", " << toString( *first ); } } oss << " }"; return oss.str(); } } } // end namespace Catch #endif // TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED