Skip to content

Instantly share code, notes, and snippets.

@martinmoene
Last active December 16, 2015 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinmoene/5425025 to your computer and use it in GitHub Desktop.
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.
// 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