mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 11:43:29 +01:00
7be8a41adf
Happening when using clang and templated operators, clang cannot decide between the operator provided by ReusableStringStream and the one provided by the value value as both are templates. This is easily solved by calling the operator<< through the member syntax. Fixes #1285
174 lines
4.9 KiB
C++
174 lines
4.9 KiB
C++
/*
|
|
* Demonstrate which version of toString/StringMaker is being used
|
|
* for various types
|
|
*/
|
|
|
|
// Replace fallback stringifier for this TU
|
|
// We should avoid ODR violations because these specific types aren't
|
|
// present in different TUs
|
|
#include <string>
|
|
template <typename T>
|
|
std::string fallbackStringifier(T const&) {
|
|
return "{ !!! }";
|
|
}
|
|
|
|
#define CATCH_CONFIG_FALLBACK_STRINGIFIER fallbackStringifier
|
|
#include "catch.hpp"
|
|
|
|
|
|
struct has_operator { };
|
|
struct has_maker {};
|
|
struct has_maker_and_operator {};
|
|
struct has_neither {};
|
|
struct has_template_operator {};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const has_operator&) {
|
|
os << "operator<<( has_operator )";
|
|
return os;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) {
|
|
os << "operator<<( has_maker_and_operator )";
|
|
return os;
|
|
}
|
|
|
|
template <typename StreamT>
|
|
StreamT& operator<<(StreamT& os, const has_template_operator&) {
|
|
os << "operator<<( has_template_operator )";
|
|
return os;
|
|
}
|
|
|
|
namespace Catch {
|
|
template<>
|
|
struct StringMaker<has_maker> {
|
|
static std::string convert( const has_maker& ) {
|
|
return "StringMaker<has_maker>";
|
|
}
|
|
};
|
|
template<>
|
|
struct StringMaker<has_maker_and_operator> {
|
|
static std::string convert( const has_maker_and_operator& ) {
|
|
return "StringMaker<has_maker_and_operator>";
|
|
}
|
|
};
|
|
}
|
|
|
|
// Call the operator
|
|
TEST_CASE( "stringify( has_operator )", "[toString]" ) {
|
|
has_operator item;
|
|
REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" );
|
|
}
|
|
|
|
// Call the stringmaker
|
|
TEST_CASE( "stringify( has_maker )", "[toString]" ) {
|
|
has_maker item;
|
|
REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" );
|
|
}
|
|
|
|
// Call the stringmaker
|
|
TEST_CASE( "stringify( has_maker_and_operator )", "[toString]" ) {
|
|
has_maker_and_operator item;
|
|
REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" );
|
|
}
|
|
|
|
TEST_CASE("stringify( has_neither )", "[toString]") {
|
|
has_neither item;
|
|
REQUIRE( ::Catch::Detail::stringify(item) == "{ !!! }" );
|
|
}
|
|
|
|
// Call the templated operator
|
|
TEST_CASE( "stringify( has_template_operator )", "[toString]" ) {
|
|
has_template_operator item;
|
|
REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_template_operator )" );
|
|
}
|
|
|
|
|
|
// Vectors...
|
|
|
|
TEST_CASE( "stringify( vectors<has_operator> )", "[toString]" ) {
|
|
std::vector<has_operator> v(1);
|
|
REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
|
|
}
|
|
|
|
TEST_CASE( "stringify( vectors<has_maker> )", "[toString]" ) {
|
|
std::vector<has_maker> v(1);
|
|
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
|
|
}
|
|
|
|
TEST_CASE( "stringify( vectors<has_maker_and_operator> )", "[toString]" ) {
|
|
std::vector<has_maker_and_operator> v(1);
|
|
REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
|
|
}
|
|
|
|
// Range-based conversion should only be used if other possibilities fail
|
|
struct int_iterator {
|
|
using iterator_category = std::input_iterator_tag;
|
|
using difference_type = std::ptrdiff_t;
|
|
using value_type = int;
|
|
using reference = int&;
|
|
using pointer = int*;
|
|
|
|
int_iterator() = default;
|
|
int_iterator(int i) :val(i) {}
|
|
|
|
value_type operator*() const { return val; }
|
|
bool operator==(int_iterator rhs) const { return val == rhs.val; }
|
|
bool operator!=(int_iterator rhs) const { return val != rhs.val; }
|
|
int_iterator operator++() { ++val; return *this; }
|
|
int_iterator operator++(int) {
|
|
auto temp(*this);
|
|
++val;
|
|
return temp;
|
|
}
|
|
private:
|
|
int val = 5;
|
|
};
|
|
|
|
struct streamable_range {
|
|
int_iterator begin() const { return int_iterator{ 1 }; }
|
|
int_iterator end() const { return {}; }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const streamable_range&) {
|
|
os << "op<<(streamable_range)";
|
|
return os;
|
|
}
|
|
|
|
struct stringmaker_range {
|
|
int_iterator begin() const { return int_iterator{ 1 }; }
|
|
int_iterator end() const { return {}; }
|
|
};
|
|
|
|
namespace Catch {
|
|
template <>
|
|
struct StringMaker<stringmaker_range> {
|
|
static std::string convert(stringmaker_range const&) {
|
|
return "stringmaker(streamable_range)";
|
|
}
|
|
};
|
|
}
|
|
|
|
struct just_range {
|
|
int_iterator begin() const { return int_iterator{ 1 }; }
|
|
int_iterator end() const { return {}; }
|
|
};
|
|
|
|
struct disabled_range {
|
|
int_iterator begin() const { return int_iterator{ 1 }; }
|
|
int_iterator end() const { return {}; }
|
|
};
|
|
|
|
namespace Catch {
|
|
template <>
|
|
struct is_range<disabled_range> {
|
|
static const bool value = false;
|
|
};
|
|
}
|
|
|
|
TEST_CASE("stringify ranges", "[toString]") {
|
|
REQUIRE(::Catch::Detail::stringify(streamable_range{}) == "op<<(streamable_range)");
|
|
REQUIRE(::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)");
|
|
REQUIRE(::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }");
|
|
REQUIRE(::Catch::Detail::stringify(disabled_range{}) == "{ !!! }");
|
|
}
|