diff --git a/IOStreams/OStreamable.test/0.cc b/IOStreams/OStreamable.test/0.cc index 5691cdb..75280cd 100644 --- a/IOStreams/OStreamable.test/0.cc +++ b/IOStreams/OStreamable.test/0.cc @@ -27,7 +27,7 @@ namespace auto - stringify( const Agg &agg, const char delim ) + stringify( const Agg &agg, const std::string delim ) { std::ostringstream oss; oss << Alepha::IOStreams::setFieldDelimiter( delim ); @@ -44,7 +44,9 @@ static auto init= Alepha::Utility::enroll <=[] "Simple OStream"_test <=TableTest< stringify > ::Cases { - { "smoke test", { { 1, 2, 3 }, '\t' }, { "1\t2\t3" } }, - { "smoke test", { { 1, 2, 3 }, ',' }, { "1,2,3" } }, + { "smoke test", { { 1, 2, 3 }, "\t" }, { "1\t2\t3" } }, + { "smoke test", { { 1, 2, 3 }, "," }, { "1,2,3" } }, + { "smoke test", { { 1, 2, 3 }, ";;" }, { "1;;2;;3" } }, + { "smoke test", { { 1, 2, 3 }, ", " }, { "1, 2, 3" } }, }; }; diff --git a/IOStreams/StreamState.h b/IOStreams/StreamState.h new file mode 100644 index 0000000..0f32f40 --- /dev/null +++ b/IOStreams/StreamState.h @@ -0,0 +1,101 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include +#include + +#include + +namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state +{ + inline namespace exports + { + template< typename Tag, typename Type > + class StreamState; + } + + template< typename Tag, typename Type > + class exports::StreamState + { + private: + static auto + index() + { + static const auto rv= std::ios::xalloc(); + return rv; + } + + static Type *& + get_ptr( std::ios_base &ios ) + { + return reinterpret_cast< Type *& >( ios.pword( index() ) ); + } + + static void + destroy( std::ios_base &ios ) + { + delete get_ptr( ios ); + get_ptr( ios )= nullptr; + } + + static void + callback_impl( const std::ios_base::event event, std::ios_base &ios, const int idx ) + { + if( idx != index() ) throw std::logic_error( "Wrong index." ); + + if( event == std::ios_base::erase_event ) destroy( ios ); + else if( event == std::ios_base::imbue_event ) + { + // Nothing to do... until I develop locale support. + } + else if( event == std::ios_base::copyfmt_event ) + { + get_ptr( ios )= new Type{ get( ios ) }; + } + } + + static void + callback( const std::ios_base::event event, std::ios_base &ios, const int idx ) noexcept + { + return callback_impl( event, ios, idx ); + } + + static void + init( std::ios_base &ios ) + { + if( not ios.iword( index() ) ) + { + ios.iword( index() )= 1; + ios.register_callback( callback, index() ); + } + auto *&ptr= get_ptr( ios ); + if( not ptr ) ptr= new Type{}; + } + + public: + static Type & + get( std::ios_base &ios ) + { + init( ios ); + return *get_ptr( ios ); + } + + struct Setter + { + const Type val; + + friend std::ostream & + operator << ( std::ostream &os, const Setter &s ) + { + StreamState::get( os )= s.val; + return os; + } + }; + }; +} + +namespace Alepha::Hydrogen::IOStreams::inline exports::inline stream_state +{ + using namespace detail::stream_state::exports; +} diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h index 955b364..097f4cd 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -10,6 +10,8 @@ static_assert( __cplusplus > 2020'00 ); #include +#include + namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters { inline namespace exports @@ -18,19 +20,21 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters enum { RecordDelimiter }; } + using FieldDelimiterState= StreamState< decltype( FieldDelimiter ), std::string >; + namespace C { - const char defaultFieldDelimiter= '\t'; + const std::string defaultFieldDelimiter= "\t"; const char defaultRecordDelimiter= '\n'; } namespace storage { - inline StaticValue< std::optional< char > > globalFieldDelimiter; + inline StaticValue< std::optional< std::string > > globalFieldDelimiter; inline StaticValue< std::optional< char > > globalRecordDelimiter; } - inline char + inline std::string globalFieldDelimiter() { if( not storage::globalFieldDelimiter().has_value() ) storage::globalFieldDelimiter()= C::defaultFieldDelimiter; @@ -43,48 +47,19 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters if( not storage::globalRecordDelimiter().has_value() ) storage::globalRecordDelimiter()= C::defaultRecordDelimiter; return storage::globalRecordDelimiter().value(); } - - - inline const int fieldIndex= std::ios::xalloc(); - - inline void - setFieldDelimiterOnIOS( std::ios &ios, const char ch ) - { - ios.iword( fieldIndex )= ch; - } - - inline char - getFieldDelimiter( std::ios &ios ) - { - if( ios.iword( fieldIndex ) == 0 ) setFieldDelimiterOnIOS( ios, globalFieldDelimiter() ); - - return ios.iword( fieldIndex ); - } inline std::ostream & operator << ( std::ostream &os, decltype( FieldDelimiter ) ) { - return os << getFieldDelimiter( os ); + return os << FieldDelimiterState::get( os ); } - struct FieldDelimiterSetter - { - const char ch; - - friend std::ostream & - operator << ( std::ostream &os, const FieldDelimiterSetter &s ) - { - setFieldDelimiterOnIOS( os, s.ch ); - return os; - } - }; - namespace exports { auto - setFieldDelimiter( const char ch ) + setFieldDelimiter( const std::string delim ) { - return FieldDelimiterSetter{ ch }; + return FieldDelimiterState::Setter{ delim }; } }