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 
20 #if defined(__GNUC__)
21 // This has to be left enabled until end of the TU, because the GCC
22 // frontend reports operator<<(std::ostream& os, const has_maker_and_operator&)
23 // as unused anyway
24 #    pragma GCC diagnostic ignored "-Wunused-function"
25 #endif
26 
27 namespace {
28 
29 struct has_operator { };
30 struct has_maker {};
31 struct has_maker_and_operator {};
32 struct has_neither {};
33 struct has_template_operator {};
34 
operator <<(std::ostream & os,const has_operator &)35 std::ostream& operator<<(std::ostream& os, const has_operator&) {
36     os << "operator<<( has_operator )";
37     return os;
38 }
39 
operator <<(std::ostream & os,const has_maker_and_operator &)40 std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) {
41     os << "operator<<( has_maker_and_operator )";
42     return os;
43 }
44 
45 template <typename StreamT>
operator <<(StreamT & os,const has_template_operator &)46 StreamT& operator<<(StreamT& os, const has_template_operator&) {
47     os << "operator<<( has_template_operator )";
48     return os;
49 }
50 
51 } // end anonymous namespace
52 
53 namespace Catch {
54     template<>
55     struct StringMaker<has_maker> {
convertCatch::StringMaker56         static std::string convert( const has_maker& ) {
57             return "StringMaker<has_maker>";
58         }
59     };
60     template<>
61     struct StringMaker<has_maker_and_operator> {
convertCatch::StringMaker62         static std::string convert( const has_maker_and_operator& ) {
63             return "StringMaker<has_maker_and_operator>";
64         }
65     };
66 }
67 
68 // Call the operator
69 TEST_CASE( "stringify( has_operator )", "[toString]" ) {
70     has_operator item;
71     REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" );
72 }
73 
74 // Call the stringmaker
75 TEST_CASE( "stringify( has_maker )", "[toString]" ) {
76     has_maker item;
77     REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" );
78 }
79 
80 // Call the stringmaker
81 TEST_CASE( "stringify( has_maker_and_operator )", "[toString]" ) {
82     has_maker_and_operator item;
83     REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" );
84 }
85 
86 TEST_CASE("stringify( has_neither )", "[toString]") {
87     has_neither item;
88     REQUIRE( ::Catch::Detail::stringify(item) == "{ !!! }" );
89 }
90 
91 // Call the templated operator
92 TEST_CASE( "stringify( has_template_operator )", "[toString]" ) {
93     has_template_operator item;
94     REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_template_operator )" );
95 }
96 
97 
98 // Vectors...
99 
100 TEST_CASE( "stringify( vectors<has_operator> )", "[toString]" ) {
101     std::vector<has_operator> v(1);
102     REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
103 }
104 
105 TEST_CASE( "stringify( vectors<has_maker> )", "[toString]" ) {
106     std::vector<has_maker> v(1);
107     REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
108 }
109 
110 TEST_CASE( "stringify( vectors<has_maker_and_operator> )", "[toString]" ) {
111     std::vector<has_maker_and_operator> v(1);
112     REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
113 }
114 
115 namespace {
116 
117 // Range-based conversion should only be used if other possibilities fail
118 struct int_iterator {
119     using iterator_category = std::input_iterator_tag;
120     using difference_type = std::ptrdiff_t;
121     using value_type = int;
122     using reference = int&;
123     using pointer = int*;
124 
125     int_iterator() = default;
int_iterator__anon77aa5e9d0211::int_iterator126     int_iterator(int i) :val(i) {}
127 
operator *__anon77aa5e9d0211::int_iterator128     value_type operator*() const { return val; }
operator ==__anon77aa5e9d0211::int_iterator129     bool operator==(int_iterator rhs) const { return val == rhs.val; }
operator !=__anon77aa5e9d0211::int_iterator130     bool operator!=(int_iterator rhs) const { return val != rhs.val; }
operator ++__anon77aa5e9d0211::int_iterator131     int_iterator operator++() { ++val; return *this; }
operator ++__anon77aa5e9d0211::int_iterator132     int_iterator operator++(int) {
133         auto temp(*this);
134         ++val;
135         return temp;
136     }
137 private:
138     int val = 5;
139 };
140 
141 struct streamable_range {
begin__anon77aa5e9d0211::streamable_range142     int_iterator begin() const { return int_iterator{ 1 }; }
end__anon77aa5e9d0211::streamable_range143     int_iterator end() const { return {}; }
144 };
145 
operator <<(std::ostream & os,const streamable_range &)146 std::ostream& operator<<(std::ostream& os, const streamable_range&) {
147     os << "op<<(streamable_range)";
148     return os;
149 }
150 
151 struct stringmaker_range {
begin__anon77aa5e9d0211::stringmaker_range152     int_iterator begin() const { return int_iterator{ 1 }; }
end__anon77aa5e9d0211::stringmaker_range153     int_iterator end() const { return {}; }
154 };
155 
156 } // end anonymous namespace
157 
158 namespace Catch {
159 template <>
160 struct StringMaker<stringmaker_range> {
convertCatch::StringMaker161     static std::string convert(stringmaker_range const&) {
162         return "stringmaker(streamable_range)";
163     }
164 };
165 }
166 
167 namespace {
168 
169 struct just_range {
begin__anon77aa5e9d0311::just_range170     int_iterator begin() const { return int_iterator{ 1 }; }
end__anon77aa5e9d0311::just_range171     int_iterator end() const { return {}; }
172 };
173 
174 struct disabled_range {
begin__anon77aa5e9d0311::disabled_range175     int_iterator begin() const { return int_iterator{ 1 }; }
end__anon77aa5e9d0311::disabled_range176     int_iterator end() const { return {}; }
177 };
178 
179 } // end anonymous namespace
180 
181 namespace Catch {
182 template <>
183 struct is_range<disabled_range> {
184     static const bool value = false;
185 };
186 }
187 
188 TEST_CASE("stringify ranges", "[toString]") {
189     REQUIRE(::Catch::Detail::stringify(streamable_range{}) == "op<<(streamable_range)");
190     REQUIRE(::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)");
191     REQUIRE(::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }");
192     REQUIRE(::Catch::Detail::stringify(disabled_range{}) == "{ !!! }");
193 }
194