• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<a id="top"></a>
2# String conversions
3
4**Contents**<br>
5[operator << overload for std::ostream](#operator--overload-for-stdostream)<br>
6[Catch::StringMaker specialisation](#catchstringmaker-specialisation)<br>
7[Catch::is_range specialisation](#catchis_range-specialisation)<br>
8[Exceptions](#exceptions)<br>
9[Enums](#enums)<br>
10[Floating point precision](#floating-point-precision)<br>
11
12
13Catch needs to be able to convert types you use in assertions and logging expressions into strings (for logging and reporting purposes).
14Most built-in or std types are supported out of the box but there are two ways that you can tell Catch how to convert your own types (or other, third-party types) into strings.
15
16## operator << overload for std::ostream
17
18This is the standard way of providing string conversions in C++ - and the chances are you may already provide this for your own purposes. If you're not familiar with this idiom it involves writing a free function of the form:
19
20```cpp
21std::ostream& operator << ( std::ostream& os, T const& value ) {
22    os << convertMyTypeToString( value );
23    return os;
24}
25```
26
27(where ```T``` is your type and ```convertMyTypeToString``` is where you'll write whatever code is necessary to make your type printable - it doesn't have to be in another function).
28
29You should put this function in the same namespace as your type, or the global namespace, and have it declared before including Catch's header.
30
31## Catch::StringMaker specialisation
32If you don't want to provide an ```operator <<``` overload, or you want to convert your type differently for testing purposes, you can provide a specialization for `Catch::StringMaker<T>`:
33
34```cpp
35namespace Catch {
36    template<>
37    struct StringMaker<T> {
38        static std::string convert( T const& value ) {
39            return convertMyTypeToString( value );
40        }
41    };
42}
43```
44
45## Catch::is_range specialisation
46As a fallback, Catch attempts to detect if the type can be iterated
47(`begin(T)` and `end(T)` are valid) and if it can be, it is stringified
48as a range. For certain types this can lead to infinite recursion, so
49it can be disabled by specializing `Catch::is_range` like so:
50
51```cpp
52namespace Catch {
53    template<>
54    struct is_range<T> {
55        static const bool value = false;
56    };
57}
58
59```
60
61
62## Exceptions
63
64By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example:
65
66```cpp
67CATCH_TRANSLATE_EXCEPTION( MyType& ex ) {
68    return ex.message();
69}
70```
71
72## Enums
73
74> Introduced in Catch 2.8.0.
75
76Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
77If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
78However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code.
79Simply provide it the (qualified) enum name, followed by all the enum values, and you're done!
80
81E.g.
82
83```cpp
84enum class Fruits { Banana, Apple, Mango };
85
86CATCH_REGISTER_ENUM( Fruits, Fruits::Banana, Fruits::Apple, Fruits::Mango )
87
88TEST_CASE() {
89    REQUIRE( Fruits::Mango == Fruits::Apple );
90}
91```
92
93... or if the enum is in a namespace:
94```cpp
95namespace Bikeshed {
96    enum class Colours { Red, Green, Blue };
97}
98
99// Important!: This macro must appear at top level scope - not inside a namespace
100// You can fully qualify the names, or use a using if you prefer
101CATCH_REGISTER_ENUM( Bikeshed::Colours,
102    Bikeshed::Colours::Red,
103    Bikeshed::Colours::Green,
104    Bikeshed::Colours::Blue )
105
106TEST_CASE() {
107    REQUIRE( Bikeshed::Colours::Red == Bikeshed::Colours::Blue );
108}
109```
110
111## Floating point precision
112
113> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch 2.8.0.
114
115Catch provides a built-in `StringMaker` specialization for both `float`
116and `double`. By default, it uses what we think is a reasonable precision,
117but you can customize it by modifying the `precision` static variable
118inside the `StringMaker` specialization, like so:
119
120```cpp
121        Catch::StringMaker<float>::precision = 15;
122        const float testFloat1 = 1.12345678901234567899f;
123        const float testFloat2 = 1.12345678991234567899f;
124        REQUIRE(testFloat1 == testFloat2);
125```
126
127This assertion will fail and print out the `testFloat1` and `testFloat2`
128to 15 decimal places.
129
130---
131
132[Home](Readme.md#top)
133