/* * Created by Phil on 17/01/2011. * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ #include "catch_common.h" #include "catch_enforce.h" #include "catch_stream.h" #include "catch_debug_console.h" #include "catch_stringref.h" #include #include #include #include #include #include #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif namespace Catch { Catch::IStream::~IStream() = default; namespace detail { namespace { template class StreamBufImpl : public std::streambuf { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() noexcept { StreamBufImpl::sync(); } private: int overflow( int c ) override { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() override { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); } return 0; } }; /////////////////////////////////////////////////////////////////////////// struct OutputDebugWriter { void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; /////////////////////////////////////////////////////////////////////////// class FileStream : public IStream { mutable std::ofstream m_ofs; public: FileStream( StringRef filename ) { m_ofs.open( filename.c_str() ); CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); } ~FileStream() override = default; public: // IStream std::ostream& stream() const override { return m_ofs; } }; /////////////////////////////////////////////////////////////////////////// class CoutStream : public IStream { mutable std::ostream m_os; public: // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream() : m_os( Catch::cout().rdbuf() ) {} ~CoutStream() override = default; public: // IStream std::ostream& stream() const override { return m_os; } }; /////////////////////////////////////////////////////////////////////////// class DebugOutStream : public IStream { std::unique_ptr> m_streamBuf; mutable std::ostream m_os; public: DebugOutStream() : m_streamBuf( new StreamBufImpl() ), m_os( m_streamBuf.get() ) {} ~DebugOutStream() override = default; public: // IStream std::ostream& stream() const override { return m_os; } }; }} // namespace anon::detail /////////////////////////////////////////////////////////////////////////// auto makeStream( StringRef const &filename ) -> IStream const* { if( filename.empty() ) return new detail::CoutStream(); else if( filename[0] == '%' ) { if( filename == "%debug" ) return new detail::DebugOutStream(); else CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); } else return new detail::FileStream( filename ); } // This class encapsulates the idea of a pool of ostringstreams that can be reused. struct StringStreams { std::vector> m_streams; std::vector m_unused; std::ostringstream m_referenceStream; // Used for copy state/ flags from static StringStreams* s_instance; auto add() -> std::size_t { if( m_unused.empty() ) { m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); return m_streams.size()-1; } else { auto index = m_unused.back(); m_unused.pop_back(); return index; } } void release( std::size_t index ) { m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state m_unused.push_back(index); } // !TBD: put in TLS static auto instance() -> StringStreams& { if( !s_instance ) s_instance = new StringStreams(); return *s_instance; } static void cleanup() { delete s_instance; s_instance = nullptr; } }; StringStreams* StringStreams::s_instance = nullptr; void ReusableStringStream::cleanup() { StringStreams::cleanup(); } ReusableStringStream::ReusableStringStream() : m_index( StringStreams::instance().add() ), m_oss( StringStreams::instance().m_streams[m_index].get() ) {} ReusableStringStream::~ReusableStringStream() { static_cast( m_oss )->str(""); m_oss->clear(); StringStreams::instance().release( m_index ); } auto ReusableStringStream::str() const -> std::string { return static_cast( m_oss )->str(); } /////////////////////////////////////////////////////////////////////////// #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions std::ostream& cout() { return std::cout; } std::ostream& cerr() { return std::cerr; } std::ostream& clog() { return std::clog; } #endif } #if defined(__clang__) # pragma clang diagnostic pop #endif