1 /*
2  * Demonstrate which version of toString/StringMaker is being used
3  * for various types
4  */
5 
6 // Replace fallback stringifier for this TU
7 // We should avoid ODR violations because these specific types aren't
8 // present in different TUs
9 #include <string>
10 template <typename T>
fallbackStringifier(T const &)11 std::string fallbackStringifier(T const&) {
12     return "{ !!! }";
13 }
14 
15 #define CATCH_CONFIG_FALLBACK_STRINGIFIER fallbackStringifier
16 #include "catch.hpp"
17 
18 
19 struct has_operator { };
20 struct has_maker {};
21 struct has_maker_and_operator {};
22 struct has_neither {};
23 
operator <<(std::ostream & os,const has_operator &)24 std::ostream& operator<<(std::ostream& os, const has_operator&) {
25     os << "operator<<( has_operator )";
26     return os;
27 }
28 
operator <<(std::ostream & os,const has_maker_and_operator &)29 std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) {
30     os << "operator<<( has_maker_and_operator )";
31     return os;
32 }
33 
34 namespace Catch {
35     template<>
36     struct StringMaker<has_maker> {
convertCatch::StringMaker37         static std::string convert( const has_maker& ) {
38             return "StringMaker<has_maker>";
39         }
40     };
41     template<>
42     struct StringMaker<has_maker_and_operator> {
convertCatch::StringMaker43         static std::string convert( const has_maker_and_operator& ) {
44             return "StringMaker<has_maker_and_operator>";
45         }
46     };
47 }
48 
49 // Call the operator
50 TEST_CASE( "stringify( has_operator )", "[toString]" ) {
51     has_operator item;
52     REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" );
53 }
54 
55 // Call the stringmaker
56 TEST_CASE( "stringify( has_maker )", "[toString]" ) {
57     has_maker item;
58     REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" );
59 }
60 
61 // Call the stringmaker
62 TEST_CASE( "stringify( has_maker_and_operator )", "[toString]" ) {
63     has_maker_and_operator item;
64     REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" );
65 }
66 
67 TEST_CASE("stringify( has_neither )", "[toString]") {
68     has_neither item;
69     REQUIRE( ::Catch::Detail::stringify(item) == "{ !!! }" );
70 }
71 
72 
73 // Vectors...
74 
75 TEST_CASE( "stringify( vectors<has_operator> )", "[toString]" ) {
76     std::vector<has_operator> v(1);
77     REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
78 }
79 
80 TEST_CASE( "stringify( vectors<has_maker> )", "[toString]" ) {
81     std::vector<has_maker> v(1);
82     REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
83 }
84 
85 TEST_CASE( "stringify( vectors<has_maker_and_operator> )", "[toString]" ) {
86     std::vector<has_maker_and_operator> v(1);
87     REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
88 }
89 
90 // Range-based conversion should only be used if other possibilities fail
91 struct int_iterator {
92     using iterator_category = std::input_iterator_tag;
93     using difference_type = std::ptrdiff_t;
94     using value_type = int;
95     using reference = int&;
96     using pointer = int*;
97 
98     int_iterator() = default;
int_iteratorint_iterator99     int_iterator(int i) :val(i) {}
100 
operator *int_iterator101     value_type operator*() const { return val; }
operator ==int_iterator102     bool operator==(int_iterator rhs) const { return val == rhs.val; }
operator !=int_iterator103     bool operator!=(int_iterator rhs) const { return val != rhs.val; }
operator ++int_iterator104     int_iterator operator++() { ++val; return *this; }
operator ++int_iterator105     int_iterator operator++(int) {
106         auto temp(*this);
107         ++val;
108         return temp;
109     }
110 private:
111     int val = 5;
112 };
113 
114 struct streamable_range {
beginstreamable_range115     int_iterator begin() const { return int_iterator{ 1 }; }
endstreamable_range116     int_iterator end() const { return {}; }
117 };
118 
operator <<(std::ostream & os,const streamable_range &)119 std::ostream& operator<<(std::ostream& os, const streamable_range&) {
120     os << "op<<(streamable_range)";
121     return os;
122 }
123 
124 struct stringmaker_range {
beginstringmaker_range125     int_iterator begin() const { return int_iterator{ 1 }; }
endstringmaker_range126     int_iterator end() const { return {}; }
127 };
128 
129 namespace Catch {
130 template <>
131 struct StringMaker<stringmaker_range> {
convertCatch::StringMaker132     static std::string convert(stringmaker_range const&) {
133         return "stringmaker(streamable_range)";
134     }
135 };
136 }
137 
138 struct just_range {
beginjust_range139     int_iterator begin() const { return int_iterator{ 1 }; }
endjust_range140     int_iterator end() const { return {}; }
141 };
142 
143 struct disabled_range {
begindisabled_range144     int_iterator begin() const { return int_iterator{ 1 }; }
enddisabled_range145     int_iterator end() const { return {}; }
146 };
147 
148 namespace Catch {
149 template <>
150 struct is_range<disabled_range> {
151     static const bool value = false;
152 };
153 }
154 
155 TEST_CASE("stringify ranges", "[toString]") {
156     REQUIRE(::Catch::Detail::stringify(streamable_range{}) == "op<<(streamable_range)");
157     REQUIRE(::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)");
158     REQUIRE(::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }");
159     REQUIRE(::Catch::Detail::stringify(disabled_range{}) == "{ !!! }");
160 }
161