Last active
December 16, 2015 11:09
-
-
Save martinmoene/5425025 to your computer and use it in GitHub Desktop.
CATCH to_string based on CATCH v0.9 build 33, with different has_insertion_operator<>.
1. 'direct' tranfer from catch_tostring.hpp.
2. Replaced stream inserter detection with has_insertion_operator<>.
3. Added conditional changes for VC6.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Notes at bottom. | |
// catch_common.h ----------------------------- | |
#ifndef TWOBLUECUBES_CATCH_COMMON_H_INCLUDED | |
#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED | |
#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line | |
#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) | |
#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) | |
#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr | |
#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) | |
#ifdef _MSC_VER | |
#define INTERNAL_CATCH_COMPILER_IS_MSVC | |
#if ( _MSC_VER >= 1200 ) && ( _MSC_VER < 1300 ) | |
#define INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
#endif | |
#endif | |
#ifdef INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
namespace std { using ::size_t; } | |
#endif | |
#endif // TWOBLUECUBES_CATCH_COMMON_H_INCLUDED | |
// (catch_meta.hpp) ------------------------------- | |
#include <vector> // NOTE:required here for VC6's is_vector<> | |
namespace Catch { | |
template <int v> | |
struct int_to_type { enum { value = v }; }; | |
struct yes_type { char c; }; | |
struct no_type { char c[2]; }; | |
} // end namespace Catch | |
#ifdef INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
#include <vector> // NOTE:required here for VC6's is_vector<> | |
namespace Catch { | |
// is_pointer | |
namespace Detail { | |
struct is_pointer_helper | |
{ | |
is_pointer_helper( const volatile void* ); | |
}; | |
yes_type is_pointer_tester( is_pointer_helper ); | |
no_type is_pointer_tester( ... ); | |
template <typename T> | |
struct is_pointer_impl | |
{ | |
static T& makeT(); | |
enum { value = sizeof( yes_type ) == sizeof( is_pointer_tester( makeT() ) ) }; | |
}; | |
} // namespace Detail | |
template < typename T > | |
struct is_pointer | |
{ | |
enum { value = Detail::is_pointer_impl<T>::value }; | |
}; | |
// is_vector | |
namespace Detail { | |
template<typename T> | |
yes_type is_vector_tester( const std::vector<T>& ); | |
no_type is_vector_tester( ... ); | |
template <typename T> | |
struct is_vector_impl | |
{ | |
static T& makeT(); | |
enum { value = sizeof( yes_type ) == sizeof( is_vector_tester( makeT() ) ) }; | |
}; | |
} // namespace Detail | |
template <typename T> | |
struct is_vector | |
{ | |
static T& makeT(); | |
enum { value = Detail::is_vector_impl<T>::value }; | |
}; | |
} // end namespace Catch | |
#endif // INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
// catch_tostring.hpp ------------------------- | |
#ifndef TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED | |
#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED | |
#include <sstream> | |
#include <iomanip> | |
#include <limits> | |
#include <vector> // NOTE:not in original catch_tostring.hpp | |
namespace Catch { | |
// has insertion operator: | |
#include <iosfwd> | |
namespace Detail { | |
struct any_t { | |
template<typename T> any_t( T const& ); | |
}; | |
no_type operator<<( std::ostream const&, any_t const& ); | |
yes_type& test( std::ostream& ); | |
no_type test( no_type ); | |
// VC6 requires name to differ from parent class | |
template<typename T> | |
struct has_insertion_operator_impl { | |
static std::ostream &s; | |
static T const &t; | |
enum { value = sizeof( test(s << t) ) == sizeof( yes_type ) }; | |
}; | |
} // end namespace Detail | |
template<typename T> | |
struct has_insertion_operator : Detail::has_insertion_operator_impl<T> {}; | |
namespace Detail { | |
typedef int_to_type<0> nonStreamable; | |
typedef int_to_type<1> Streamable; | |
template<typename T> | |
void toStream( std::ostream& os, T const& value, Streamable ) { | |
os << value; | |
} | |
template<typename T> | |
void toStream( std::ostream& os, T const&, nonStreamable ) { | |
os << "{?}"; | |
} | |
} // end namespace Detail | |
template<typename T> | |
struct StringMaker { | |
static std::string convert( T const& value ) { | |
std::ostringstream oss; | |
Detail::toStream( oss, value, int_to_type<has_insertion_operator<T>::value>() ); | |
return oss.str(); | |
} | |
}; | |
#ifdef INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
/* | |
* VC6 does not support partial template specialisation, so we select the | |
* proper StringMaker via StringMakerSelector<>. | |
*/ | |
template<typename T> | |
struct StringMakerPtr { | |
static std::string convert( T const * const p ) { | |
if( !p ) | |
return "(NULL)"; // return INTERNAL_CATCH_STRINGIFY( NULL ); | |
std::ostringstream oss; | |
oss << p; | |
return oss.str(); | |
} | |
}; | |
template<typename T> | |
struct StringMakerVec { | |
static std::string convert( std::vector<T> const& v ) { | |
std::ostringstream oss; | |
oss << "{ "; | |
for( std::size_t i = 0; i < v.size(); ++ i ) { | |
oss << toString( v[i] ); // Catch:: | |
if( i < v.size() - 1 ) | |
oss << ", "; | |
} | |
oss << " }"; | |
return oss.str(); | |
} | |
}; | |
namespace Detail { | |
typedef int_to_type<1> is_value_t; | |
typedef int_to_type<2> is_pointer_t; | |
typedef int_to_type<3> is_vector_t; | |
template < typename T > | |
struct StringMakerSelector | |
{ | |
enum { value = is_vector<T>::value ? (int) is_vector_t::value | |
: is_pointer<T>::value ? (int) is_pointer_t::value | |
: (int) is_value_t::value | |
}; | |
}; | |
template<typename T> | |
inline std::string makeString( T const & value, is_value_t* ) | |
{ | |
return StringMaker<T>::convert( value ); | |
} | |
template<typename T> | |
inline std::string makeString( T const * const pointer, is_pointer_t* ) | |
{ | |
return StringMakerPtr<T>::convert( pointer ); | |
} | |
template<typename T> | |
inline std::string makeString( std::vector<T> const & vec, is_vector_t* ) | |
{ | |
return StringMakerVec<T>::convert( vec ); | |
} | |
template<typename T> | |
inline std::string makeString( T const & value ) | |
{ | |
return makeString( value, ( int_to_type<StringMakerSelector<T>::value >* )0 ); | |
} | |
} // namespace Detail | |
template<typename T> | |
inline std::string toString( T const & value ) | |
{ | |
return Detail::makeString( value ); | |
} | |
#else // INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
template<typename T> | |
struct StringMaker<T*> { | |
static std::string convert( T const* p ) { | |
if( !p ) | |
return INTERNAL_CATCH_STRINGIFY( NULL ); | |
std::ostringstream oss; | |
oss << p; | |
return oss.str(); | |
} | |
}; | |
template<typename T> | |
struct StringMaker<std::vector<T> > { | |
static std::string convert( std::vector<T> const& v ) { | |
std::ostringstream oss; | |
oss << "{ "; | |
for( std::size_t i = 0; i < v.size(); ++ i ) { | |
oss << toString( v[i] ); | |
if( i < v.size() - 1 ) | |
oss << ", "; | |
} | |
oss << " }"; | |
return oss.str(); | |
} | |
}; | |
namespace Detail { | |
template<typename T> | |
inline std::string makeString( const T& value ) { | |
return StringMaker<T>::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<typename T> | |
std::string toString( const T& value ) { | |
return StringMaker<T>::convert( value ); | |
} | |
#endif // INTERNAL_CATCH_COMPILER_IS_MSVC6 | |
// Built in overloads | |
inline std::string toString( const std::string& value ) { | |
return "\"" + value + "\""; | |
} | |
inline std::string toString( const std::wstring& value ) { | |
std::ostringstream oss; | |
oss << "\""; | |
for(size_t i = 0; i < value.size(); ++i ) | |
oss << static_cast<char>( value[i] <= 0xff ? value[i] : '?'); | |
oss << "\""; | |
return oss.str(); | |
} | |
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<const char*>( 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<unsigned long>( value ) ); | |
} | |
inline std::string toString( const double value ) { | |
std::ostringstream oss; | |
oss << std::setprecision (std::numeric_limits<double>::digits10 + 1) | |
<< value; | |
return oss.str(); | |
} | |
inline std::string toString( bool value ) { | |
return value ? "true" : "false"; | |
} | |
inline std::string toString( char value ) { | |
return value < ' ' | |
? toString( static_cast<unsigned int>( value ) ) | |
: Detail::makeString( value ); | |
} | |
inline std::string toString( signed char value ) { | |
return toString( static_cast<char>( value ) ); | |
} | |
inline std::string toString( unsigned char value ) { | |
return toString( static_cast<char>( value ) ); | |
} | |
#ifdef CATCH_CONFIG_CPP11_NULLPTR | |
inline std::string toString( std::nullptr_t ) { | |
return "nullptr"; | |
} | |
#endif | |
} // namespace Catch | |
#endif // TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED | |
// program ------------------------------------ | |
#include <iostream> | |
using namespace Catch; | |
struct S {}; | |
struct X {}; | |
std::ostream & operator<<( std::ostream & os, S const & ) { return os << "S"; } | |
int main(void) | |
{ | |
std::vector<int> vect; vect.push_back(3); vect.push_back(7); | |
std::cout << | |
"toString( S() ): " << toString( S() ) << std::endl << | |
"toString( X() ): " << toString( X() ) << std::endl << | |
"toString( true ): " << toString( true ) << std::endl << | |
"toString( std::string(\"hello\") ): " << toString( std::string("hello") ) << std::endl << | |
"toString( \"world\" ): " << toString( "world" ) << std::endl << | |
"toString( (char*)0 ): " << toString( (char*)0 ) << std::endl << | |
"toString( (int*)NULL ): " << toString( (int*)NULL ) << std::endl << | |
"toString( (void*)NULL ): " << toString( (void*)NULL ) << std::endl << | |
"toString( 42 ): " << toString( 42 ) << std::endl << | |
"toString( 42UL ): " << toString( 42UL ) << std::endl << | |
"toString( 9876UL ): " << toString( 9876UL ) << std::endl << | |
"toString( 3.14 ): " << toString( 3.14 ) << std::endl << | |
"toString( 'a' ): " << toString( 'a' ) << std::endl << | |
"toString( char(' '-1) ) : " << toString( char(' '-1) ) << std::endl << | |
"toString( vect ): " << toString( vect ) << std::endl << | |
std::endl; | |
return 0; // VC6 | |
} | |
// Notes: | |
// ------------ | |
// Objective-C code omitted. | |
// Replaced stream inserter detection with has_insertion_operator<>. | |
// clang 3.1 stops working after transfer of catch_tostring.hpp into | |
// this program; don't know why. | |
// | |
// clang 3.1: in StringMaker<std::vector<T> >: | |
// catch_tostring.cpp:68:20: error: call to function 'toString' that is neither | |
// visible in the template definition nor found by argument-dependent lookup | |
// Compilation: | |
// ------------ | |
// cl -nologo -W3 -EHsc catch_tostring.cpp && catch_tostring | |
// g++ -Wall -o catch_tostring.exe catch_tostring.cpp && catch_tostring | |
// clang++ -Wall -o catch_tostring.exe catch_tostring.cpp && catch_tostring |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment