diff --git a/IOStreams/IStreamable.h b/IOStreams/IStreamable.h index 8b2c2c4..d9a1c32 100644 --- a/IOStreams/IStreamable.h +++ b/IOStreams/IStreamable.h @@ -34,7 +34,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: istreamable_module const auto commentChar= line.find( "#" ); if( commentChar != std::string::npos ) line= line.substr( line.find( "#" ) ); - const auto delim= getFieldDelimiter( is ); + const auto delim= getDelimiter( fieldDelimiter, is ); const auto tokens= split( line, delim ); auto decomposed= Alepha::Reflection::tuplizeAggregate( istreamable ); diff --git a/IOStreams/OStreamable.h b/IOStreams/OStreamable.h index 0e3661f..fb91b26 100644 --- a/IOStreams/OStreamable.h +++ b/IOStreams/OStreamable.h @@ -45,7 +45,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: ostreamable_module // aggregates, so we'll go with this simple case for now... tuple_for_each( decomposed ) <=[&]( const auto &element ) { - if( not first ) os << FieldDelimiter; + if( not first ) os << fieldDelimiter; first= false; os << element; }; diff --git a/IOStreams/OStreamable.test/0.cc b/IOStreams/OStreamable.test/0.cc index 103b723..cffc0c7 100644 --- a/IOStreams/OStreamable.test/0.cc +++ b/IOStreams/OStreamable.test/0.cc @@ -31,8 +31,9 @@ namespace stringify_specific( const Agg &agg, const std::string delim ) { std::ostringstream oss; - Alepha::IOStreams::setGlobalFieldDelimiter( "YOU SHOULD NOT SEE THIS" ); - oss << Alepha::IOStreams::setFieldDelimiter( delim ); + using Alepha::IOStreams::fieldDelimiter; + Alepha::IOStreams::setGlobalDelimiter( fieldDelimiter, "YOU SHOULD NOT SEE THIS" ); + oss << Alepha::IOStreams::setDelimiter( fieldDelimiter, delim ); oss << agg; return std::move( oss ).str(); } @@ -41,7 +42,8 @@ namespace stringify_global( const Agg &agg, const std::string delim ) { std::ostringstream oss; - Alepha::IOStreams::setGlobalFieldDelimiter( delim ); + using Alepha::IOStreams::fieldDelimiter; + Alepha::IOStreams::setGlobalDelimiter( fieldDelimiter, delim ); oss << agg; return std::move( oss ).str(); } @@ -81,8 +83,8 @@ static auto init= Alepha::Utility::enroll <=[] []( const Agg agg, const std::string delim ) { using Alepha::IOStreams::String; - using Alepha::IOStreams::setFieldDelimiter; - return String{} << setFieldDelimiter( delim ) << agg << FinishString; + using Alepha::IOStreams::fieldDelimiter; + return String{} << setDelimiter( fieldDelimiter, delim ) << agg << FinishString; } > ::Cases diff --git a/IOStreams/StreamState.h b/IOStreams/StreamState.h index 5366819..e2d7e77 100644 --- a/IOStreams/StreamState.h +++ b/IOStreams/StreamState.h @@ -11,47 +11,53 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state { inline namespace exports { - template< typename Tag, typename Type, auto Default= [] { return Type{}; } > + template< typename > class StreamState; } - template< typename Tag, typename Type, auto Default > - class exports::StreamState + template< typename Type > + class exports::StreamState : boost::noncopyable { private: - static auto - index() + const int index= std::ios::xalloc(); + std::function< Type () > build; + + public: + explicit + StreamState( const std::function< Type () > build ) + : build( build ) {} + + private: + static Type *& + get_ptr( std::ios_base &ios, const int idx ) { - static const auto rv= std::ios::xalloc(); - return rv; + return reinterpret_cast< Type *& >( ios.pword( idx ) ); } - static Type *& + Type *& get_ptr( std::ios_base &ios ) { - return reinterpret_cast< Type *& >( ios.pword( index() ) ); + return get_ptr( ios, index ); } static void - destroy( std::ios_base &ios ) + destroy( std::ios_base &ios, const int idx ) { - delete get_ptr( ios ); - get_ptr( ios )= nullptr; + delete get_ptr( ios, idx ); + get_ptr( ios, idx )= 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 ); + if( event == std::ios_base::erase_event ) destroy( ios, idx ); 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 ) }; + get_ptr( ios, idx )= new Type{ *get_ptr( ios, idx ) }; } } @@ -61,44 +67,57 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state return callback_impl( event, ios, idx ); } - static void + void init( std::ios_base &ios ) { - if( not ios.iword( index() ) ) + if( not ios.iword( index ) ) { - ios.iword( index() )= 1; - ios.register_callback( callback, index() ); + ios.iword( index )= 1; + ios.register_callback( callback, index ); } - auto *&ptr= get_ptr( ios ); - if( not ptr ) ptr= new Type{ Default() }; + auto *&ptr= get_ptr( ios, index ); + if( not ptr ) ptr= new Type{ build() }; } public: - static Type & + Type & get( std::ios_base &ios ) { init( ios ); return *get_ptr( ios ); } + void + setDefault( const Type value ) + { + build= [value] { return value; }; + } + struct Setter { + StreamState *state; const Type val; friend std::ostream & operator << ( std::ostream &os, const Setter &s ) { - StreamState::get( os )= s.val; + s.state->get( os )= s.val; return os; } friend std::istream & operator >> ( std::istream &is, const Setter &s ) { - StreamState::get( is )= s.val; + s.state->get( is )= s.val; return is; } }; + + auto + makeSetter( const Type val ) + { + return Setter{ this, val }; + } }; } diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h index becbd99..8cffc76 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -12,117 +12,61 @@ static_assert( __cplusplus > 2020'00 ); #include -namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters +namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters_m { - inline namespace exports + inline namespace exports {} + + // Syntax I want to work: + // + // std::cout << someDelimiter; + // auto value= getDelimiter( someDelimiter, std::cin ); + // std::cout << setDelimiter( someDelimiter, value ); + + struct Delimiter : boost::noncopyable { - enum { FieldDelimiter }; - enum { RecordDelimiter }; - } + StreamState< std::string > state; - namespace C - { - const std::string defaultFieldDelimiter= "\t"; - const char defaultRecordDelimiter= '\n'; - } - - namespace storage - { - inline StaticValue< std::optional< std::string > > globalFieldDelimiter; - inline StaticValue< std::optional< char > > globalRecordDelimiter; - } - - inline std::string - globalFieldDelimiter() - { - if( not storage::globalFieldDelimiter().has_value() ) storage::globalFieldDelimiter()= C::defaultFieldDelimiter; - return storage::globalFieldDelimiter().value(); - } - - namespace exports - { - inline void - setGlobalFieldDelimiter( const std::string delim ) - { - storage::globalFieldDelimiter()= delim; - } - } - - inline char - globalRecordDelimiter() - { - if( not storage::globalRecordDelimiter().has_value() ) storage::globalRecordDelimiter()= C::defaultRecordDelimiter; - return storage::globalRecordDelimiter().value(); - } - - using FieldDelimiterState= StreamState< decltype( FieldDelimiter ), std::string, globalFieldDelimiter >; - - inline std::ostream & - operator << ( std::ostream &os, decltype( FieldDelimiter ) ) - { - return os << FieldDelimiterState::get( os ); - } - - namespace exports - { - auto - setFieldDelimiter( const std::string delim ) - { - return FieldDelimiterState::Setter{ delim }; - } - - const auto & - getFieldDelimiter( std::ios_base &ios ) - { - return FieldDelimiterState::get( ios ); - } - } - - inline const int recordIndex= std::ios::xalloc(); - - inline void - setRecordDelimiterOnIOS( std::ios &ios, const char ch ) - { - ios.iword( recordIndex )= ch; - } - - inline char - getRecordDelimiter( std::ios &ios ) - { - if( ios.iword( recordIndex ) == 0 ) setRecordDelimiterOnIOS( ios, globalRecordDelimiter() ); - - return ios.iword( recordIndex ); - } - - inline std::ostream & - operator << ( std::ostream &os, decltype( RecordDelimiter ) ) - { - return os << getRecordDelimiter( os ); - } - - struct RecordDelimiterSetter - { - const char ch; - - friend std::ostream & - operator << ( std::ostream &os, const RecordDelimiterSetter &s ) - { - setRecordDelimiterOnIOS( os, s.ch ); - return os; - } + explicit + Delimiter( const std::string dflt ) + : state( [dflt] { return dflt; } ) {} }; namespace exports { - auto - setRecordDelimiter( const char ch ) + inline Delimiter fieldDelimiter{ "\t" }; + inline Delimiter recordDelimiter{ "\n" }; + } + + inline std::ostream & + operator << ( std::ostream &os, Delimiter &delim ) + { + const auto &s= delim.state.get( os ); + return os << s; + } + + namespace exports + { + inline std::string + getDelimiter( Delimiter &delim, std::ios_base &ios ) { - return RecordDelimiterSetter{ ch }; + return delim.state.get( ios ); + } + + inline auto + setDelimiter( Delimiter &delim, const std::string s ) + { + return delim.state.makeSetter( s ); + } + + inline void + setGlobalDelimiter( Delimiter &delim, const std::string s ) + { + return delim.state.setDefault( s ); } } } -namespace Alepha::Hydrogen::IOStreams::inline exports::inline delimiters +namespace Alepha::Hydrogen::IOStreams::inline exports::inline delimiters_m { - using namespace detail::delimiters::exports; + using namespace detail::delimiters_m::exports; } diff --git a/IOStreams/streamable.test/0.cc b/IOStreams/streamable.test/0.cc index c0b2e17..28f3269 100644 --- a/IOStreams/streamable.test/0.cc +++ b/IOStreams/streamable.test/0.cc @@ -31,14 +31,14 @@ namespace std::string roundTripString( const std::string text, const std::string delim ) { - using namespace Alepha::IOStreams::exports::delimiters; + using namespace Alepha::IOStreams::exports::delimiters_m; std::istringstream iss{ text }; Agg agg; - iss >> setFieldDelimiter( delim ) >> agg; + iss >> setDelimiter( fieldDelimiter, delim ) >> agg; std::ostringstream oss; - oss << setFieldDelimiter( delim ) << agg; + oss << setDelimiter( fieldDelimiter, delim ) << agg; return oss.str(); }