123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- * 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 "catch_singletons.hpp"
- #include <cstdio>
- #include <iostream>
- #include <fstream>
- #include <sstream>
- #include <vector>
- #include <memory>
- namespace Catch {
- Catch::IStream::~IStream() = default;
- namespace Detail { namespace {
- template<typename WriterF, std::size_t bufferSize=256>
- 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<char>( c ) ) );
- else
- sputc( static_cast<char>( c ) );
- }
- return 0;
- }
- int sync() override {
- if( pbase() != pptr() ) {
- m_writer( std::string( pbase(), static_cast<std::string::size_type>( 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<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
- mutable std::ostream m_os;
- public:
- DebugOutStream()
- : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
- 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<std::unique_ptr<std::ostringstream>> m_streams;
- std::vector<std::size_t> m_unused;
- std::ostringstream m_referenceStream; // Used for copy state/ flags from
- auto add() -> std::size_t {
- if( m_unused.empty() ) {
- m_streams.push_back( std::unique_ptr<std::ostringstream>( 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);
- }
- };
- ReusableStringStream::ReusableStringStream()
- : m_index( Singleton<StringStreams>::getMutable().add() ),
- m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
- {}
- ReusableStringStream::~ReusableStringStream() {
- static_cast<std::ostringstream*>( m_oss )->str("");
- m_oss->clear();
- Singleton<StringStreams>::getMutable().release( m_index );
- }
- auto ReusableStringStream::str() const -> std::string {
- return static_cast<std::ostringstream*>( 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
- }
|