forked from Alepha/Alepha
Relocate StackableStreambuf
to IOStreams.
This commit is contained in:
@ -1,5 +0,0 @@
|
||||
target_sources( alepha PRIVATE
|
||||
StackableStreambuf.cc
|
||||
)
|
||||
|
||||
add_subdirectory( StackableStreambuf.test )
|
||||
|
@ -1,113 +0,0 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include "StackableStreambuf.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const auto index= std::ios::xalloc();
|
||||
|
||||
auto *&
|
||||
getStackPtr( std::ios_base &ios )
|
||||
{
|
||||
return reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
|
||||
}
|
||||
|
||||
auto &
|
||||
getStack( std::ios_base &ios )
|
||||
{
|
||||
auto &ownership= getStackPtr( ios );
|
||||
if( not ownership ) ownership= new std::decay_t< decltype( *ownership ) >{};
|
||||
|
||||
return *ownership;
|
||||
}
|
||||
|
||||
enum { token };
|
||||
|
||||
inline bool
|
||||
releaseTop( std::ios_base &ios, decltype( token )= token )
|
||||
{
|
||||
auto &ownership= getStack( ios );
|
||||
|
||||
assert( not ownership.empty() );
|
||||
// Since it's owned, delete happens at scope exit.
|
||||
const std::unique_ptr top= std::move( ownership.top() );
|
||||
ownership.pop();
|
||||
|
||||
top->drain();
|
||||
return not ownership.empty();
|
||||
}
|
||||
|
||||
inline bool
|
||||
releaseTop( std::ostream &os )
|
||||
{
|
||||
const auto *const current= dynamic_cast< StackableStreambuf * >( os.rdbuf() );
|
||||
if( not current ) return false;
|
||||
|
||||
os.rdbuf( current->underlying );
|
||||
|
||||
releaseTop( os, token );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
releaseStack( std::ios_base &ios )
|
||||
{
|
||||
delete getStackPtr( ios );
|
||||
getStackPtr( ios )= nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
iosCallback( const std::ios_base::event event, std::ios_base &ios, const int idx )
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wterminate"
|
||||
|
||||
if( index != idx ) throw std::logic_error{ "Wrong index." };
|
||||
|
||||
if( event == std::ios_base::erase_event ) releaseStack( ios );
|
||||
else if( event == std::ios_base::imbue_event ) {}
|
||||
else if( event == std::ios_base::copyfmt_event ) throw std::runtime_error{ "Can't copy?" };
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
impl::operator << ( std::ostream &os, PopStack )
|
||||
{
|
||||
if( not releaseTop( os ) ) throw std::logic_error( "OStream has no stacked streambufs!" );
|
||||
return os;
|
||||
}
|
||||
|
||||
StackableStreambuf::~StackableStreambuf() {}
|
||||
|
||||
StackableStreambuf::StackableStreambuf( std::ostream &host )
|
||||
: underlying( host.rdbuf( this ) )
|
||||
{
|
||||
// TODO: Atomicity for this:
|
||||
if( not host.iword( index ) ) host.register_callback( iosCallback, index );
|
||||
host.iword( index )= 1;
|
||||
getStack( host ).emplace( this );
|
||||
}
|
||||
|
||||
int
|
||||
StackableStreambuf::overflow( const int ch )
|
||||
{
|
||||
if( ch == EOF ) throw std::logic_error( "EOF!" );
|
||||
writeChar( ch );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::streamsize
|
||||
StackableStreambuf::xsputn( const char *const data, const std::streamsize amt )
|
||||
{
|
||||
for( std::streamsize i= 0; i< amt; ++i ) overflow( data[ i ] );
|
||||
return amt;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
|
||||
namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
||||
{
|
||||
inline namespace exports
|
||||
{
|
||||
struct StackableStreambuf;
|
||||
|
||||
template< typename > struct PushStack;
|
||||
|
||||
struct PopStack{};
|
||||
}
|
||||
|
||||
struct exports::StackableStreambuf
|
||||
: virtual public std::streambuf
|
||||
{
|
||||
public:
|
||||
std::streambuf *underlying;
|
||||
|
||||
~StackableStreambuf() override;
|
||||
|
||||
// Children must be created by `new`.
|
||||
explicit StackableStreambuf( std::ostream &host );
|
||||
|
||||
auto out() const { return std::ostream{ underlying }; }
|
||||
|
||||
virtual void writeChar( char ch )= 0;
|
||||
virtual void drain()= 0;
|
||||
|
||||
int overflow( const int ch ) override;
|
||||
|
||||
std::streamsize xsputn( const char *data, std::streamsize amt ) override;
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct exports::PushStack
|
||||
: T
|
||||
{
|
||||
using T::T;
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
std::ostream &
|
||||
operator << ( std::ostream &os, PushStack< T > &¶ms )
|
||||
{
|
||||
build_streambuf( os, std::move( params ) );
|
||||
return os;
|
||||
}
|
||||
|
||||
inline namespace impl
|
||||
{
|
||||
std::ostream & operator << ( std::ostream &os, PopStack );
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Hydrogen::Utility::inline exports::inline stackable_streambuf
|
||||
{
|
||||
using namespace detail::stackable_streambuf::exports;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include "../StackableStreambuf.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <Alepha/Testing/test.h>
|
||||
#include <Alepha/Testing/TableTest.h>
|
||||
#include <Alepha/Utility/evaluation_helpers.h>
|
||||
|
||||
#include <Alepha/word_wrap.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace Alepha::Testing::literals::test_literals;;
|
||||
using Alepha::Testing::TableTest;
|
||||
}
|
||||
|
||||
static auto init= Alepha::Utility::enroll <=[]
|
||||
{
|
||||
"Simple stacked wordwrap case."_test <=[]
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << Alepha::StartWrap{ 20 };
|
||||
oss << "First wrapping\n";
|
||||
oss << Alepha::StartWrap{ 10 };
|
||||
oss << "Second wrapping\n";
|
||||
oss << Alepha::EndWrap;
|
||||
oss << "Third wrapping more than 20\n";
|
||||
oss << Alepha::EndWrap;
|
||||
|
||||
std::ofstream log{ "log.txt" };
|
||||
log << oss.str();
|
||||
log.close();
|
||||
std::cout << oss.str() << std::flush;
|
||||
assert( oss.str() == "First wrapping\nSecond \nwrapping\nThird wrapping more \nthan 20\n" );
|
||||
};
|
||||
};
|
@ -1 +0,0 @@
|
||||
unit_test( 0 )
|
Reference in New Issue
Block a user