1
0
forked from Alepha/Alepha

Make StackableStreambuf a compiled library.

This commit is contained in:
2023-10-20 04:44:19 -04:00
parent 0b63658815
commit 0001117f78
4 changed files with 118 additions and 103 deletions

View File

@ -1 +1,5 @@
target_sources( alepha PRIVATE
StackableStreambuf.cc
)
add_subdirectory( StackableStreambuf.test )

View File

@ -0,0 +1,105 @@
static_assert( __cplusplus > 2020'00 );
#include "StackableStreambuf.h"
namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
{
namespace
{
const auto index= std::ios::xalloc();
inline auto &
getStack( std::ios_base &ios )
{
auto &ownership= reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
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 );
// 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 )
{
auto &os= dynamic_cast< std::ostream & >( ios );
while( releaseTop( os ) );
}
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;
}
}

View File

@ -18,23 +18,7 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
template< typename > struct PushStack;
template< typename > struct PopStack;
}
inline const auto index= std::ios::xalloc();
inline namespace impl
{
void iosCallback( const std::ios_base::event event, std::ios_base &ios, const int idx );
}
inline auto &
getStack( std::ios_base &ios )
{
auto &ownership= reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
if( not ownership ) ownership= new std::decay_t< decltype( *ownership ) >{};
return *ownership;
struct PopStack{};
}
struct exports::StackableStreambuf
@ -43,92 +27,21 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
public:
std::streambuf *underlying;
~StackableStreambuf() override {}
~StackableStreambuf() override;
// Children must be created by `new`.
explicit
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 );
}
explicit StackableStreambuf( std::ostream &host );
auto out() const { return std::ostream{ underlying }; }
virtual void writeChar( const char ch )= 0;
virtual void writeChar( char ch )= 0;
virtual void drain()= 0;
int
overflow( const int ch ) override
{
if( ch == EOF ) throw std::logic_error( "EOF!" );
writeChar( ch );
int overflow( const int ch ) override;
return 1;
}
std::streamsize
xsputn( const char *const data, const std::streamsize amt ) override
{
for( std::streamsize i= 0; i< amt; ++i ) overflow( data[ i ] );
return amt;
}
std::streamsize xsputn( const char *data, std::streamsize amt ) override;
};
enum { token };
inline bool
releaseTop( std::ios_base &ios, decltype( token )= token )
{
auto &ownership= getStack( ios );
// 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 )
{
auto &os= dynamic_cast< std::ostream & >( ios );
while( releaseTop( os ) );
}
inline void
impl::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
}
template< typename T >
struct exports::PushStack
: T
@ -136,9 +49,6 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
using T::T;
};
template< typename T >
struct exports::PopStack {};
template< typename T >
std::ostream &
operator << ( std::ostream &os, PushStack< T > &&params )
@ -147,14 +57,10 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
return os;
}
template< typename T >
std::ostream &
operator << ( std::ostream &os, PopStack< T > )
inline namespace impl
{
if( not releaseTop( os ) ) throw std::logic_error( "OStream has no stacked streambufs!" );
return os;
std::ostream & operator << ( std::ostream &os, PopStack );
}
}
namespace Alepha::Hydrogen::Utility::inline exports::inline stackable_streambuf

View File

@ -25,7 +25,7 @@ namespace Alepha::inline Cavorite ::detail:: word_wrap
using StartWrap= Utility::PushStack< StartWrap_params >;
constexpr Utility::PopStack< StartWrap_params > EndWrap;
constexpr Utility::PopStack EndWrap;
}
inline namespace impl