1 /* 2 * Created by Phil on 17/01/2011. 3 * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 * 8 */ 9 10 #include "catch_common.h" 11 #include "catch_enforce.h" 12 #include "catch_stream.h" 13 #include "catch_debug_console.h" 14 #include "catch_stringref.h" 15 16 #include <cstdio> 17 #include <iostream> 18 #include <fstream> 19 #include <sstream> 20 #include <vector> 21 #include <memory> 22 23 #if defined(__clang__) 24 # pragma clang diagnostic push 25 # pragma clang diagnostic ignored "-Wexit-time-destructors" 26 #endif 27 28 namespace Catch { 29 30 Catch::IStream::~IStream() = default; 31 32 namespace detail { namespace { 33 template<typename WriterF, std::size_t bufferSize=256> 34 class StreamBufImpl : public std::streambuf { 35 char data[bufferSize]; 36 WriterF m_writer; 37 38 public: StreamBufImpl()39 StreamBufImpl() { 40 setp( data, data + sizeof(data) ); 41 } 42 ~StreamBufImpl()43 ~StreamBufImpl() noexcept { 44 StreamBufImpl::sync(); 45 } 46 47 private: overflow(int c)48 int overflow( int c ) override { 49 sync(); 50 51 if( c != EOF ) { 52 if( pbase() == epptr() ) 53 m_writer( std::string( 1, static_cast<char>( c ) ) ); 54 else 55 sputc( static_cast<char>( c ) ); 56 } 57 return 0; 58 } 59 sync()60 int sync() override { 61 if( pbase() != pptr() ) { 62 m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); 63 setp( pbase(), epptr() ); 64 } 65 return 0; 66 } 67 }; 68 69 /////////////////////////////////////////////////////////////////////////// 70 71 struct OutputDebugWriter { 72 operator ()Catch::detail::__anon55f7acec0111::OutputDebugWriter73 void operator()( std::string const&str ) { 74 writeToDebugConsole( str ); 75 } 76 }; 77 78 /////////////////////////////////////////////////////////////////////////// 79 80 class FileStream : public IStream { 81 mutable std::ofstream m_ofs; 82 public: FileStream(StringRef filename)83 FileStream( StringRef filename ) { 84 m_ofs.open( filename.c_str() ); 85 CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); 86 } 87 ~FileStream() override = default; 88 public: // IStream stream() const89 std::ostream& stream() const override { 90 return m_ofs; 91 } 92 }; 93 94 /////////////////////////////////////////////////////////////////////////// 95 96 class CoutStream : public IStream { 97 mutable std::ostream m_os; 98 public: 99 // Store the streambuf from cout up-front because 100 // cout may get redirected when running tests CoutStream()101 CoutStream() : m_os( Catch::cout().rdbuf() ) {} 102 ~CoutStream() override = default; 103 104 public: // IStream stream() const105 std::ostream& stream() const override { return m_os; } 106 }; 107 108 /////////////////////////////////////////////////////////////////////////// 109 110 class DebugOutStream : public IStream { 111 std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; 112 mutable std::ostream m_os; 113 public: DebugOutStream()114 DebugOutStream() 115 : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), 116 m_os( m_streamBuf.get() ) 117 {} 118 119 ~DebugOutStream() override = default; 120 121 public: // IStream stream() const122 std::ostream& stream() const override { return m_os; } 123 }; 124 125 }} // namespace anon::detail 126 127 /////////////////////////////////////////////////////////////////////////// 128 makeStream(StringRef const & filename)129 auto makeStream( StringRef const &filename ) -> IStream const* { 130 if( filename.empty() ) 131 return new detail::CoutStream(); 132 else if( filename[0] == '%' ) { 133 if( filename == "%debug" ) 134 return new detail::DebugOutStream(); 135 else 136 CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); 137 } 138 else 139 return new detail::FileStream( filename ); 140 } 141 142 143 // This class encapsulates the idea of a pool of ostringstreams that can be reused. 144 struct StringStreams { 145 std::vector<std::unique_ptr<std::ostringstream>> m_streams; 146 std::vector<std::size_t> m_unused; 147 std::ostringstream m_referenceStream; // Used for copy state/ flags from 148 static StringStreams* s_instance; 149 addCatch::StringStreams150 auto add() -> std::size_t { 151 if( m_unused.empty() ) { 152 m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); 153 return m_streams.size()-1; 154 } 155 else { 156 auto index = m_unused.back(); 157 m_unused.pop_back(); 158 return index; 159 } 160 } 161 releaseCatch::StringStreams162 void release( std::size_t index ) { 163 m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state 164 m_unused.push_back(index); 165 } 166 167 // !TBD: put in TLS instanceCatch::StringStreams168 static auto instance() -> StringStreams& { 169 if( !s_instance ) 170 s_instance = new StringStreams(); 171 return *s_instance; 172 } cleanupCatch::StringStreams173 static void cleanup() { 174 delete s_instance; 175 s_instance = nullptr; 176 } 177 }; 178 179 StringStreams* StringStreams::s_instance = nullptr; 180 cleanup()181 void ReusableStringStream::cleanup() { 182 StringStreams::cleanup(); 183 } 184 ReusableStringStream()185 ReusableStringStream::ReusableStringStream() 186 : m_index( StringStreams::instance().add() ), 187 m_oss( StringStreams::instance().m_streams[m_index].get() ) 188 {} 189 ~ReusableStringStream()190 ReusableStringStream::~ReusableStringStream() { 191 static_cast<std::ostringstream*>( m_oss )->str(""); 192 m_oss->clear(); 193 StringStreams::instance().release( m_index ); 194 } 195 str() const196 auto ReusableStringStream::str() const -> std::string { 197 return static_cast<std::ostringstream*>( m_oss )->str(); 198 } 199 200 201 /////////////////////////////////////////////////////////////////////////// 202 203 204 #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions cout()205 std::ostream& cout() { return std::cout; } cerr()206 std::ostream& cerr() { return std::cerr; } clog()207 std::ostream& clog() { return std::clog; } 208 #endif 209 } 210 211 #if defined(__clang__) 212 # pragma clang diagnostic pop 213 #endif 214