catch_stream.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. #include "catch_common.h"
  10. #include "catch_enforce.h"
  11. #include "catch_stream.h"
  12. #include "catch_debug_console.h"
  13. #include "catch_stringref.h"
  14. #include "catch_singletons.hpp"
  15. #include <cstdio>
  16. #include <iostream>
  17. #include <fstream>
  18. #include <sstream>
  19. #include <vector>
  20. #include <memory>
  21. namespace Catch {
  22. Catch::IStream::~IStream() = default;
  23. namespace Detail { namespace {
  24. template<typename WriterF, std::size_t bufferSize=256>
  25. class StreamBufImpl : public std::streambuf {
  26. char data[bufferSize];
  27. WriterF m_writer;
  28. public:
  29. StreamBufImpl() {
  30. setp( data, data + sizeof(data) );
  31. }
  32. ~StreamBufImpl() noexcept {
  33. StreamBufImpl::sync();
  34. }
  35. private:
  36. int overflow( int c ) override {
  37. sync();
  38. if( c != EOF ) {
  39. if( pbase() == epptr() )
  40. m_writer( std::string( 1, static_cast<char>( c ) ) );
  41. else
  42. sputc( static_cast<char>( c ) );
  43. }
  44. return 0;
  45. }
  46. int sync() override {
  47. if( pbase() != pptr() ) {
  48. m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
  49. setp( pbase(), epptr() );
  50. }
  51. return 0;
  52. }
  53. };
  54. ///////////////////////////////////////////////////////////////////////////
  55. struct OutputDebugWriter {
  56. void operator()( std::string const&str ) {
  57. writeToDebugConsole( str );
  58. }
  59. };
  60. ///////////////////////////////////////////////////////////////////////////
  61. class FileStream : public IStream {
  62. mutable std::ofstream m_ofs;
  63. public:
  64. FileStream( StringRef filename ) {
  65. m_ofs.open( filename.c_str() );
  66. CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
  67. }
  68. ~FileStream() override = default;
  69. public: // IStream
  70. std::ostream& stream() const override {
  71. return m_ofs;
  72. }
  73. };
  74. ///////////////////////////////////////////////////////////////////////////
  75. class CoutStream : public IStream {
  76. mutable std::ostream m_os;
  77. public:
  78. // Store the streambuf from cout up-front because
  79. // cout may get redirected when running tests
  80. CoutStream() : m_os( Catch::cout().rdbuf() ) {}
  81. ~CoutStream() override = default;
  82. public: // IStream
  83. std::ostream& stream() const override { return m_os; }
  84. };
  85. ///////////////////////////////////////////////////////////////////////////
  86. class DebugOutStream : public IStream {
  87. std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
  88. mutable std::ostream m_os;
  89. public:
  90. DebugOutStream()
  91. : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
  92. m_os( m_streamBuf.get() )
  93. {}
  94. ~DebugOutStream() override = default;
  95. public: // IStream
  96. std::ostream& stream() const override { return m_os; }
  97. };
  98. }} // namespace anon::detail
  99. ///////////////////////////////////////////////////////////////////////////
  100. auto makeStream( StringRef const &filename ) -> IStream const* {
  101. if( filename.empty() )
  102. return new Detail::CoutStream();
  103. else if( filename[0] == '%' ) {
  104. if( filename == "%debug" )
  105. return new Detail::DebugOutStream();
  106. else
  107. CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
  108. }
  109. else
  110. return new Detail::FileStream( filename );
  111. }
  112. // This class encapsulates the idea of a pool of ostringstreams that can be reused.
  113. struct StringStreams {
  114. std::vector<std::unique_ptr<std::ostringstream>> m_streams;
  115. std::vector<std::size_t> m_unused;
  116. std::ostringstream m_referenceStream; // Used for copy state/ flags from
  117. auto add() -> std::size_t {
  118. if( m_unused.empty() ) {
  119. m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
  120. return m_streams.size()-1;
  121. }
  122. else {
  123. auto index = m_unused.back();
  124. m_unused.pop_back();
  125. return index;
  126. }
  127. }
  128. void release( std::size_t index ) {
  129. m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
  130. m_unused.push_back(index);
  131. }
  132. };
  133. ReusableStringStream::ReusableStringStream()
  134. : m_index( Singleton<StringStreams>::getMutable().add() ),
  135. m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
  136. {}
  137. ReusableStringStream::~ReusableStringStream() {
  138. static_cast<std::ostringstream*>( m_oss )->str("");
  139. m_oss->clear();
  140. Singleton<StringStreams>::getMutable().release( m_index );
  141. }
  142. auto ReusableStringStream::str() const -> std::string {
  143. return static_cast<std::ostringstream*>( m_oss )->str();
  144. }
  145. ///////////////////////////////////////////////////////////////////////////
  146. #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
  147. std::ostream& cout() { return std::cout; }
  148. std::ostream& cerr() { return std::cerr; }
  149. std::ostream& clog() { return std::clog; }
  150. #endif
  151. }