forked from Alepha/Alepha
Make StackableStreambuf a compiled library.
This commit is contained in:
@ -1 +1,5 @@
|
|||||||
|
target_sources( alepha PRIVATE
|
||||||
|
StackableStreambuf.cc
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory( StackableStreambuf.test )
|
add_subdirectory( StackableStreambuf.test )
|
||||||
|
|||||||
105
Utility/StackableStreambuf.cc
Normal file
105
Utility/StackableStreambuf.cc
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,23 +18,7 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
|
|
||||||
template< typename > struct PushStack;
|
template< typename > struct PushStack;
|
||||||
|
|
||||||
template< typename > struct PopStack;
|
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 exports::StackableStreambuf
|
struct exports::StackableStreambuf
|
||||||
@ -43,92 +27,21 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
public:
|
public:
|
||||||
std::streambuf *underlying;
|
std::streambuf *underlying;
|
||||||
|
|
||||||
~StackableStreambuf() override {}
|
~StackableStreambuf() override;
|
||||||
|
|
||||||
// Children must be created by `new`.
|
// Children must be created by `new`.
|
||||||
explicit
|
explicit StackableStreambuf( std::ostream &host );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto out() const { return std::ostream{ underlying }; }
|
auto out() const { return std::ostream{ underlying }; }
|
||||||
|
|
||||||
virtual void writeChar( const char ch )= 0;
|
virtual void writeChar( char ch )= 0;
|
||||||
virtual void drain()= 0;
|
virtual void drain()= 0;
|
||||||
|
|
||||||
int
|
int overflow( const int ch ) override;
|
||||||
overflow( const int ch ) override
|
|
||||||
{
|
|
||||||
if( ch == EOF ) throw std::logic_error( "EOF!" );
|
|
||||||
writeChar( ch );
|
|
||||||
|
|
||||||
return 1;
|
std::streamsize xsputn( const char *data, std::streamsize amt ) override;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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 >
|
template< typename T >
|
||||||
struct exports::PushStack
|
struct exports::PushStack
|
||||||
: T
|
: T
|
||||||
@ -136,9 +49,6 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
using T::T;
|
using T::T;
|
||||||
};
|
};
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
struct exports::PopStack {};
|
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
std::ostream &
|
std::ostream &
|
||||||
operator << ( std::ostream &os, PushStack< T > &¶ms )
|
operator << ( std::ostream &os, PushStack< T > &¶ms )
|
||||||
@ -147,14 +57,10 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
template< typename T >
|
inline namespace impl
|
||||||
std::ostream &
|
|
||||||
operator << ( std::ostream &os, PopStack< T > )
|
|
||||||
{
|
{
|
||||||
if( not releaseTop( os ) ) throw std::logic_error( "OStream has no stacked streambufs!" );
|
std::ostream & operator << ( std::ostream &os, PopStack );
|
||||||
return os;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility::inline exports::inline stackable_streambuf
|
namespace Alepha::Hydrogen::Utility::inline exports::inline stackable_streambuf
|
||||||
|
|||||||
@ -25,7 +25,7 @@ namespace Alepha::inline Cavorite ::detail:: word_wrap
|
|||||||
|
|
||||||
using StartWrap= Utility::PushStack< StartWrap_params >;
|
using StartWrap= Utility::PushStack< StartWrap_params >;
|
||||||
|
|
||||||
constexpr Utility::PopStack< StartWrap_params > EndWrap;
|
constexpr Utility::PopStack EndWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline namespace impl
|
inline namespace impl
|
||||||
|
|||||||
Reference in New Issue
Block a user