forked from Alepha/Alepha
Further improvement to StackableStreambuf.
The ownership model is more shored up.
This commit is contained in:
@ -7,6 +7,8 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
||||||
{
|
{
|
||||||
@ -23,19 +25,25 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
|
|
||||||
inline namespace impl
|
inline namespace impl
|
||||||
{
|
{
|
||||||
void releaseStack( std::ios_base &ios );
|
|
||||||
bool releaseTop( std::ostream &os );
|
|
||||||
|
|
||||||
void iosCallback( const std::ios_base::event event, std::ios_base &ios, const int idx );
|
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
|
||||||
: virtual public std::streambuf
|
: virtual public std::streambuf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::streambuf *underlying;
|
std::streambuf *underlying;
|
||||||
|
|
||||||
~StackableStreambuf() {}
|
~StackableStreambuf() override {}
|
||||||
|
|
||||||
// Children must be created by `new`.
|
// Children must be created by `new`.
|
||||||
explicit
|
explicit
|
||||||
@ -45,9 +53,10 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
// TODO: Atomicity for this:
|
// TODO: Atomicity for this:
|
||||||
if( not host.iword( index ) ) host.register_callback( iosCallback, index );
|
if( not host.iword( index ) ) host.register_callback( iosCallback, index );
|
||||||
host.iword( index )= 1;
|
host.iword( index )= 1;
|
||||||
|
getStack( host ).emplace( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream out() const { return std::ostream{ underlying }; }
|
auto out() const { return std::ostream{ underlying }; }
|
||||||
|
|
||||||
virtual void writeChar( const char ch )= 0;
|
virtual void writeChar( const char ch )= 0;
|
||||||
virtual void drain()= 0;
|
virtual void drain()= 0;
|
||||||
@ -69,22 +78,36 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool
|
enum { token };
|
||||||
impl::releaseTop( std::ostream &os )
|
|
||||||
{
|
inline bool
|
||||||
auto *const streambuf= os.rdbuf();
|
releaseTop( std::ios_base &ios, decltype( token )= token )
|
||||||
if( not streambuf ) return false;
|
{
|
||||||
auto *const stacked= dynamic_cast< StackableStreambuf * >( streambuf );
|
auto &ownership= getStack( ios );
|
||||||
if( not stacked ) return false;
|
|
||||||
|
// 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 );
|
||||||
|
|
||||||
stacked->drain();
|
|
||||||
os.rdbuf( stacked->underlying );
|
|
||||||
delete stacked;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
impl::releaseStack( std::ios_base &ios )
|
releaseStack( std::ios_base &ios )
|
||||||
{
|
{
|
||||||
auto &os= dynamic_cast< std::ostream & >( ios );
|
auto &os= dynamic_cast< std::ostream & >( ios );
|
||||||
|
|
||||||
@ -94,9 +117,16 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
|||||||
inline void
|
inline void
|
||||||
impl::iosCallback( const std::ios_base::event event, std::ios_base &ios, const int idx )
|
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( index != idx ) throw std::logic_error{ "Wrong index." };
|
||||||
|
|
||||||
if( event == std::ios_base::erase_event ) releaseStack( ios );
|
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 >
|
||||||
|
|||||||
@ -7,6 +7,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "AutoRAII.h"
|
||||||
|
|
||||||
namespace Alepha::Cavorite ::detail:: string_algorithms
|
namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||||
{
|
{
|
||||||
@ -27,7 +28,8 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
|||||||
VarMap substitutions;
|
VarMap substitutions;
|
||||||
std::stringbuf varName;
|
std::stringbuf varName;
|
||||||
char sigil;
|
char sigil;
|
||||||
enum { Symbol, Normal } mode= Normal;
|
enum { Symbol= 1, Normal= 0 } mode= Normal;
|
||||||
|
int throws= 0;
|
||||||
|
|
||||||
void
|
void
|
||||||
writeChar( const char ch )
|
writeChar( const char ch )
|
||||||
@ -80,32 +82,51 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
|||||||
void
|
void
|
||||||
drain()
|
drain()
|
||||||
{
|
{
|
||||||
if( mode != Normal ) throw std::runtime_error{ "Unterminated variable `" + varName.str() + " in expansion." };
|
std::cerr << "Drain called, and mode is: " << mode << std::endl;
|
||||||
|
if( mode != Normal )
|
||||||
|
{
|
||||||
|
std::cerr << "Mode not being normal, we're throwing (" << ++throws << " times now)..." << std::endl;
|
||||||
|
mode= Normal;
|
||||||
|
throw std::runtime_error{ "Unterminated variable `" + varName.str() + " in expansion." };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto wrapperIndex= std::ios::xalloc();
|
const auto wrapperIndex= std::ios::xalloc();
|
||||||
|
|
||||||
void
|
void
|
||||||
releaseWrapper( std::ios_base &ios )
|
releaseWrapper( std::ostream &os )
|
||||||
{
|
{
|
||||||
auto *const streambuf= static_cast< VariableExpansionStreambuf * >( ios.pword( wrapperIndex ) );
|
std::cerr << "Release wrapper called on: " << &os << std::endl;
|
||||||
|
auto *const streambuf= static_cast< VariableExpansionStreambuf * >( os.pword( wrapperIndex ) );
|
||||||
if( not streambuf ) throw std::logic_error{ "Attempt to remove a substitution context which doesn't exist." };
|
if( not streambuf ) throw std::logic_error{ "Attempt to remove a substitution context which doesn't exist." };
|
||||||
|
|
||||||
|
AutoRAII current
|
||||||
|
{
|
||||||
|
[&] { return os.rdbuf( streambuf->underlying ); },
|
||||||
|
[&] ( std::streambuf *streambuf ) noexcept
|
||||||
|
{
|
||||||
|
std::cerr << "Deletion actually happening, now." << std::endl;
|
||||||
|
delete streambuf;
|
||||||
|
os.pword( wrapperIndex )= nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
streambuf->drain();
|
streambuf->drain();
|
||||||
dynamic_cast< std::ostream & >( ios ).rdbuf( streambuf->underlying );
|
|
||||||
delete streambuf;
|
|
||||||
ios.pword( wrapperIndex )= nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
wordwrapCallback( const std::ios_base::event event, std::ios_base &ios, const int idx )
|
wordwrapCallback( const std::ios_base::event event, std::ios_base &ios, const int idx ) noexcept
|
||||||
{
|
{
|
||||||
|
std::cerr << "ios callback called on: " << &ios << std::endl;
|
||||||
if( wrapperIndex != idx ) throw std::logic_error{ "Wrong index." };
|
if( wrapperIndex != idx ) throw std::logic_error{ "Wrong index." };
|
||||||
|
|
||||||
if( not ios.pword( wrapperIndex ) ) return;
|
if( not ios.pword( wrapperIndex ) ) return;
|
||||||
|
|
||||||
if( event == std::ios_base::erase_event ) releaseWrapper( ios );
|
|
||||||
|
if( const auto os_p= dynamic_cast< std::ostream * >( &ios ); os_p and event == std::ios_base::erase_event )
|
||||||
|
{
|
||||||
|
releaseWrapper( *os_p );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +142,7 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
|||||||
{
|
{
|
||||||
state= 1;
|
state= 1;
|
||||||
os.register_callback( wordwrapCallback, wrapperIndex );
|
os.register_callback( wordwrapCallback, wrapperIndex );
|
||||||
|
std::cerr << "Adding callback to " << (void *) static_cast< std::ios * >( &os ) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( os.pword( wrapperIndex ) == nullptr );
|
assert( os.pword( wrapperIndex ) == nullptr );
|
||||||
|
|||||||
@ -25,6 +25,11 @@ static auto init= enroll <=[]
|
|||||||
{ "$H$ $W$", { { "H", lambaste<="Hello" }, { "W", lambaste<="World" } }, '$' },
|
{ "$H$ $W$", { { "H", lambaste<="Hello" }, { "W", lambaste<="World" } }, '$' },
|
||||||
"Hello World"
|
"Hello World"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Hello World (with some spaces)",
|
||||||
|
{ "$with space$ $can-expand$", { { "with space", lambaste<="Hello" }, { "can-expand", lambaste<="World" } }, '$' },
|
||||||
|
"Hello World"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"Hello $$ World",
|
"Hello $$ World",
|
||||||
|
|||||||
@ -111,38 +111,6 @@ namespace Alepha::Cavorite ::detail:: word_wrap
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const auto wrapperIndex= std::ios_base::xalloc();
|
|
||||||
|
|
||||||
void
|
|
||||||
releaseWrapper( std::ios_base &ios )
|
|
||||||
{
|
|
||||||
auto *const streambuf= static_cast< WordWrapStreambuf * >( ios.pword( wrapperIndex ) );
|
|
||||||
streambuf->drain();
|
|
||||||
dynamic_cast< std::ostream & >( ios ).rdbuf( streambuf->underlying );
|
|
||||||
delete streambuf;
|
|
||||||
ios.pword( wrapperIndex )= nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
wordwrapCallback( const std::ios_base::event event, std::ios_base &ios, const int idx ) noexcept
|
|
||||||
{
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wterminate"
|
|
||||||
|
|
||||||
if( wrapperIndex != idx ) throw std::logic_error( "Must only work with the word wrap index." );
|
|
||||||
|
|
||||||
if( not ios.pword( wrapperIndex ) ) return;
|
|
||||||
|
|
||||||
if( event == std::ios_base::erase_event ) releaseWrapper( 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
impl::build_streambuf( std::ostream &os, StartWrap &&args )
|
impl::build_streambuf( std::ostream &os, StartWrap &&args )
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user