mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			203 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  *  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 <cstdio>
 | |
| #include <iostream>
 | |
| #include <fstream>
 | |
| #include <sstream>
 | |
| #include <vector>
 | |
| #include <memory>
 | |
| 
 | |
| #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<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);
 | |
|         }
 | |
| 
 | |
|         // !TBD: put in TLS
 | |
|         static auto instance() -> StringStreams& {
 | |
|             static StringStreams s_stringStreams;
 | |
|             return s_stringStreams;
 | |
|         }
 | |
|     };
 | |
| 
 | |
| 
 | |
|     ReusableStringStream::ReusableStringStream()
 | |
|     :   m_index( StringStreams::instance().add() ),
 | |
|         m_oss( StringStreams::instance().m_streams[m_index].get() )
 | |
|     {}
 | |
| 
 | |
|     ReusableStringStream::~ReusableStringStream() {
 | |
|         static_cast<std::ostringstream*>( m_oss )->str("");
 | |
|         m_oss->clear();
 | |
|         StringStreams::instance().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
 | |
| }
 | |
| 
 | |
| #if defined(__clang__)
 | |
| #    pragma clang diagnostic pop
 | |
| #endif
 |