1 /*
2  *  Created by Phil on 8/5/2012.
3  *  Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 #ifndef TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
9 #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
10 
11 #include "catch_common.h"
12 
13 #include <sstream>
14 #include <iomanip>
15 #include <limits>
16 #include <vector>
17 #include <cstddef>
18 
19 #ifdef __OBJC__
20 #include "catch_objc_arc.hpp"
21 #endif
22 
23 #ifdef CATCH_CONFIG_CPP11_TUPLE
24 #include <tuple>
25 #endif
26 
27 #ifdef CATCH_CONFIG_CPP11_IS_ENUM
28 #include <type_traits>
29 #endif
30 
31 namespace Catch {
32 
33 // Why we're here.
34 template<typename T>
35 std::string toString( T const& value );
36 
37 // Built in overloads
38 
39 std::string toString( std::string const& value );
40 std::string toString( std::wstring const& value );
41 std::string toString( const char* const value );
42 std::string toString( char* const value );
43 std::string toString( const wchar_t* const value );
44 std::string toString( wchar_t* const value );
45 std::string toString( int value );
46 std::string toString( unsigned long value );
47 std::string toString( unsigned int value );
48 std::string toString( const double value );
49 std::string toString( const float value );
50 std::string toString( bool value );
51 std::string toString( char value );
52 std::string toString( signed char value );
53 std::string toString( unsigned char value );
54 
55 #ifdef CATCH_CONFIG_CPP11_LONG_LONG
56 std::string toString( long long value );
57 std::string toString( unsigned long long value );
58 #endif
59 
60 #ifdef CATCH_CONFIG_CPP11_NULLPTR
61 std::string toString( std::nullptr_t );
62 #endif
63 
64 #ifdef __OBJC__
65     std::string toString( NSString const * const& nsstring );
66     std::string toString( NSString * CATCH_ARC_STRONG & nsstring );
67     std::string toString( NSObject* const& nsObject );
68 #endif
69 
70 
71 namespace Detail {
72 
73     extern const std::string unprintableString;
74 
75  #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
76     struct BorgType {
77         template<typename T> BorgType( T const& );
78     };
79 
80     struct TrueType { char sizer[1]; };
81     struct FalseType { char sizer[2]; };
82 
83     TrueType& testStreamable( std::ostream& );
84     FalseType testStreamable( FalseType );
85 
86     FalseType operator<<( std::ostream const&, BorgType const& );
87 
88     template<typename T>
89     struct IsStreamInsertable {
90         static std::ostream &s;
91         static T  const&t;
92         enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
93     };
94 #else
95     template<typename T>
96     class IsStreamInsertable {
97         template<typename SS, typename TT>
98         static auto test(int)
99         -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
100 
101         template<typename, typename>
102         static auto test(...) -> std::false_type;
103 
104     public:
105         static const bool value = decltype(test<std::ostream,const T&>(0))::value;
106     };
107 #endif
108 
109 #if defined(CATCH_CONFIG_CPP11_IS_ENUM)
110     template<typename T,
111              bool IsEnum = std::is_enum<T>::value
112              >
113     struct EnumStringMaker
114     {
convertEnumStringMaker115         static std::string convert( T const& ) { return unprintableString; }
116     };
117 
118     template<typename T>
119     struct EnumStringMaker<T,true>
120     {
121         static std::string convert( T const& v )
122         {
123             return ::Catch::toString(
124                 static_cast<typename std::underlying_type<T>::type>(v)
125                 );
126         }
127     };
128 #endif
129     template<bool C>
130     struct StringMakerBase {
131 #if defined(CATCH_CONFIG_CPP11_IS_ENUM)
132         template<typename T>
133         static std::string convert( T const& v )
134         {
135             return EnumStringMaker<T>::convert( v );
136         }
137 #else
138         template<typename T>
139         static std::string convert( T const& ) { return unprintableString; }
140 #endif
141     };
142 
143     template<>
144     struct StringMakerBase<true> {
145         template<typename T>
146         static std::string convert( T const& _value ) {
147             std::ostringstream oss;
148             oss << _value;
149             return oss.str();
150         }
151     };
152 
153     std::string rawMemoryToString( const void *object, std::size_t size );
154 
155     template<typename T>
156     std::string rawMemoryToString( const T& object ) {
157       return rawMemoryToString( &object, sizeof(object) );
158     }
159 
160 } // end namespace Detail
161 
162 template<typename T>
163 struct StringMaker :
164     Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
165 
166 template<typename T>
167 struct StringMaker<T*> {
168     template<typename U>
169     static std::string convert( U* p ) {
170         if( !p )
171             return "NULL";
172         else
173             return Detail::rawMemoryToString( p );
174     }
175 };
176 
177 template<typename R, typename C>
178 struct StringMaker<R C::*> {
179     static std::string convert( R C::* p ) {
180         if( !p )
181             return "NULL";
182         else
183             return Detail::rawMemoryToString( p );
184     }
185 };
186 
187 namespace Detail {
188     template<typename InputIterator>
189     std::string rangeToString( InputIterator first, InputIterator last );
190 }
191 
192 //template<typename T, typename Allocator>
193 //struct StringMaker<std::vector<T, Allocator> > {
194 //    static std::string convert( std::vector<T,Allocator> const& v ) {
195 //        return Detail::rangeToString( v.begin(), v.end() );
196 //    }
197 //};
198 
199 template<typename T, typename Allocator>
200 std::string toString( std::vector<T,Allocator> const& v ) {
201     return Detail::rangeToString( v.begin(), v.end() );
202 }
203 
204 
205 #ifdef CATCH_CONFIG_CPP11_TUPLE
206 
207 // toString for tuples
208 namespace TupleDetail {
209   template<
210       typename Tuple,
211       std::size_t N = 0,
212       bool = (N < std::tuple_size<Tuple>::value)
213       >
214   struct ElementPrinter {
215       static void print( const Tuple& tuple, std::ostream& os )
216       {
217           os << ( N ? ", " : " " )
218              << Catch::toString(std::get<N>(tuple));
219           ElementPrinter<Tuple,N+1>::print(tuple,os);
220       }
221   };
222 
223   template<
224       typename Tuple,
225       std::size_t N
226       >
227   struct ElementPrinter<Tuple,N,false> {
228       static void print( const Tuple&, std::ostream& ) {}
229   };
230 
231 }
232 
233 template<typename ...Types>
234 struct StringMaker<std::tuple<Types...>> {
235 
236     static std::string convert( const std::tuple<Types...>& tuple )
237     {
238         std::ostringstream os;
239         os << '{';
240         TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
241         os << " }";
242         return os.str();
243     }
244 };
245 #endif // CATCH_CONFIG_CPP11_TUPLE
246 
247 namespace Detail {
248     template<typename T>
249     std::string makeString( T const& value ) {
250         return StringMaker<T>::convert( value );
251     }
252 } // end namespace Detail
253 
254 /// \brief converts any type to a string
255 ///
256 /// The default template forwards on to ostringstream - except when an
257 /// ostringstream overload does not exist - in which case it attempts to detect
258 /// that and writes {?}.
259 /// Overload (not specialise) this template for custom typs that you don't want
260 /// to provide an ostream overload for.
261 template<typename T>
262 std::string toString( T const& value ) {
263     return StringMaker<T>::convert( value );
264 }
265 
266 
267     namespace Detail {
268     template<typename InputIterator>
269     std::string rangeToString( InputIterator first, InputIterator last ) {
270         std::ostringstream oss;
271         oss << "{ ";
272         if( first != last ) {
273             oss << Catch::toString( *first );
274             for( ++first ; first != last ; ++first )
275                 oss << ", " << Catch::toString( *first );
276         }
277         oss << " }";
278         return oss.str();
279     }
280 }
281 
282 } // end namespace Catch
283 
284 #endif // TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
285