diff --git a/IOStreams/StackableStreambuf.cc b/IOStreams/StackableStreambuf.cc index 64837f5..809bbb3 100644 --- a/IOStreams/StackableStreambuf.cc +++ b/IOStreams/StackableStreambuf.cc @@ -4,6 +4,9 @@ static_assert( __cplusplus > 2020'99 ); #include +#include +#include + namespace Alepha::Hydrogen::IOStreams::detail::StackableStreambuf_m { namespace @@ -47,13 +50,26 @@ namespace Alepha::Hydrogen::IOStreams::detail::StackableStreambuf_m const auto *const current= dynamic_cast< StackableStreambuf * >( os.rdbuf() ); if( not current ) return false; - os.rdbuf( current->underlying ); + current->unhook( os ); releaseTop( os, token ); return true; } + inline bool + releaseTop( std::istream &is ) + { + const auto *const current= dynamic_cast< StackableStreambuf * >( is.rdbuf() ); + if( not current ) return false; + + current->unhook( is ); + + releaseTop( is, token ); + + return true; + } + inline void releaseStack( std::ios_base &ios ) { @@ -84,6 +100,13 @@ namespace Alepha::Hydrogen::IOStreams::detail::StackableStreambuf_m return os; } + std::istream & + impl::operator >> ( std::istream &is, PopStack ) + { + if( not releaseTop( is ) ) throw std::logic_error( "IStream has no stacked streambufs!" ); + return is; + } + StackableStreambuf::~StackableStreambuf() {} StackableStreambuf::StackableStreambuf( std::ios &host ) @@ -111,4 +134,9 @@ namespace Alepha::Hydrogen::IOStreams::detail::StackableStreambuf_m for( std::streamsize i= 0; i< amt; ++i ) overflow( data[ i ] ); return amt; } + + void StackableStreambuf::unhook( std::ostream &os ) const { os.rdbuf( underlying ); } + void StackableStreambuf::unhook( std::istream &is ) const { is.rdbuf( underlying ); } + + std::ostream &StackableStreambuf::out( std::ostream &&res ) const { res.rdbuf( underlying ); return *&res; } } diff --git a/IOStreams/StackableStreambuf.h b/IOStreams/StackableStreambuf.h index 6baee68..50ffc17 100644 --- a/IOStreams/StackableStreambuf.h +++ b/IOStreams/StackableStreambuf.h @@ -24,19 +24,24 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: StackableStreambuf_m struct exports::StackableStreambuf : virtual public std::streambuf { - public: + private: std::streambuf *underlying; + public: ~StackableStreambuf() override; // Children must be created by `new`. explicit StackableStreambuf( std::ios &host ); - auto out() const { return std::ostream{ underlying }; } - virtual void writeChar( char ch )= 0; virtual void drain()= 0; + void unhook( std::ostream &os ) const; + void unhook( std::istream &is ) const; + + protected: + std::ostream &out( std::ostream &&res= std::ostream{nullptr} ) const; + int overflow( const int ch ) override; // Underflow is not overridden. A read-oriented diff --git a/ProgramOptions.cc b/ProgramOptions.cc index 6c6d759..6d121d8 100644 --- a/ProgramOptions.cc +++ b/ProgramOptions.cc @@ -10,6 +10,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include + #include #include @@ -132,13 +133,13 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m namespace { - std::string - buildIncompatibleHelpText( const std::string &name, const auto &domains, const auto &exclusivityMembers ) + void + incompatibleHelpText( std::ostream &out, const std::string &name, const auto &domains, const auto &exclusivityMembers ) { if( not domains.contains( typeid( ExclusivityDomain ) ) or domains.at( typeid( ExclusivityDomain ) ).empty() ) { - return ""; + return; } std::set< std::string > incompatibles; @@ -150,18 +151,17 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m []( const auto &item ) { return item.second; } ); } incompatibles.erase( name ); - if( incompatibles.empty() ) return ""; - std::ostringstream oss; - oss << "\nIncompatible with: \n\n"; + if( incompatibles.empty() ) return; + + out << "\nIncompatible with: \n\n"; bool first= true; for( const auto &incompat: incompatibles ) { - if( not first ) oss << ", "; + if( not first ) out << ", "; first= false; - oss << '`' << incompat << '`'; + out << '`' << incompat << '`'; } - oss << std::endl; - return std::move( oss ).str(); + out << std::endl; } void @@ -229,7 +229,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m std::cout << name << ": " << std::string( padding, ' ' ) << helpText.str() << '\n'; // Append the incompatibility text, when we see mutually-exclusive options. - std::cout << buildIncompatibleHelpText( name, domains, exclusivityMembers ); + incompatibleHelpText( std::cout, name, domains, exclusivityMembers ); } diff --git a/string_algorithms.cc b/string_algorithms.cc index 094fd93..d007a18 100644 --- a/string_algorithms.cc +++ b/string_algorithms.cc @@ -42,7 +42,7 @@ namespace Alepha::Hydrogen ::detail:: string_algorithms_m writeChar( const char ch ) override { if( C::debugExpansion ) error() << "Entry to write and got `" << ch << '`' << std::endl; - std::ostream current{ underlying }; + std::ostream current{ out().rdbuf() }; if( mode == Normal and ch == sigil ) { diff --git a/word_wrap.cc b/word_wrap.cc index 628997a..dce8e69 100644 --- a/word_wrap.cc +++ b/word_wrap.cc @@ -64,28 +64,27 @@ namespace Alepha::Hydrogen ::detail:: word_wrap_m void WordWrapStreambuf::writeChar( const char ch ) { - std::ostream outWrap{ underlying }; if( ch == '\n' ) { const auto prev= currentLineLength; const auto size= currentWord.size(); - currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), out() ); currentWord.clear(); - outWrap << '\n'; + out() << '\n'; if( currentLineLength == prev + size ) { - std::fill_n( std::ostream_iterator< char >{ outWrap }, nextLineOffset, ' ' ); + std::fill_n( std::ostream_iterator< char >{ out() }, nextLineOffset, ' ' ); currentLineLength= nextLineOffset; } else currentLineLength= 0; } else if( ch == ' ' ) { - currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + currentLineLength= applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), out() ); currentWord.clear(); if( currentLineLength < maximumWidth ) { - outWrap << ' '; + out() << ' '; ++currentLineLength; } } @@ -96,8 +95,7 @@ namespace Alepha::Hydrogen ::detail:: word_wrap_m WordWrapStreambuf::drain() { if( currentWord.empty() ) return; - std::ostream outWrap{ underlying }; - applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), outWrap ); + applyWordToLine( maximumWidth, nextLineOffset, currentLineLength, std::move( currentWord ), out() ); } std::string