From 4a74921cb8b058ff703bc37db7397e580fa76082 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 31 Oct 2023 01:44:34 -0400 Subject: [PATCH 1/2] I started a weird rewrite, but I'll likely abandon it. But I'll keep this commit in my history, in case I want to try again. --- IOStreams/StreamState.h | 46 +++++++++--------- IOStreams/delimiters.h | 102 +++++++++++++++++++++++++++++----------- 2 files changed, 97 insertions(+), 51 deletions(-) diff --git a/IOStreams/StreamState.h b/IOStreams/StreamState.h index 5366819..e538f0e 100644 --- a/IOStreams/StreamState.h +++ b/IOStreams/StreamState.h @@ -15,43 +15,42 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state class StreamState; } - template< typename Tag, typename Type, auto Default > - class exports::StreamState + template< typename Type > + class exports::StreamState : boost::noncopyable { private: - static auto - index() - { - static const auto rv= std::ios::xalloc(); - return rv; - } + const int index= std::ios::xalloc(); static Type *& + get_ptr( std::ios_base &ios, const int idx ) + { + return reinterpret_cast< Type *& >( ios.pword( idx ) ); + } + + Type *& get_ptr( std::ios_base &ios ) { - return reinterpret_cast< Type *& >( ios.pword( index() ) ); + 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( ios, idx ) }; } } @@ -61,20 +60,20 @@ 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( idx ) ) { ios.iword( index() )= 1; - ios.register_callback( callback, index() ); + ios.register_callback( callback, idx ); } - auto *&ptr= get_ptr( ios ); + auto *&ptr= get_ptr( ios, idx ); if( not ptr ) ptr= new Type{ Default() }; } public: - static Type & + Type & get( std::ios_base &ios ) { init( ios ); @@ -83,19 +82,20 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state struct Setter { + StreamState *state; const Type val; friend std::ostream & operator << ( std::ostream &os, const Setter &s ) { - StreamState::get( os )= s.val; + s.get( os )= s.val; return os; } friend std::istream & operator >> ( std::istream &is, const Setter &s ) { - StreamState::get( is )= s.val; + s.get( is )= s.val; return is; } }; diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h index becbd99..91c5a08 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -12,55 +12,101 @@ static_assert( __cplusplus > 2020'00 ); #include -namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters +namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters_m { + 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 + { + std::string current; + StreamState state; + + explicit + Delimiter( const std::string current ) + : current( current ) + { + } + }; + + template< ConstexprString s > + using CreateDelimiter= void ( const DelimiterParamType< s > & ); + + template< typename T > + constexpr is_delimiter_v= false; + + template< ConstexprString s > + constexpr is_delimiter_v< CreateDelimiter< s > >; + + inline StaticValue< std::map< void (*)( const DelimiterBase & ), StreamState< Delim, std::string, globalDelimiter< + + + template< typename Delimiter > + using DelimiterStateByValue= StreamState< Delim, std::string, globalDelimiter< Delim > >; + inline namespace exports { - enum { FieldDelimiter }; - enum { RecordDelimiter }; - } + Delimiter< "\t"_cs > FieldDelimiter; + Delimiter< "\t"_cs > FieldDelimiter2; - namespace C - { - const std::string defaultFieldDelimiter= "\t"; - const char defaultRecordDelimiter= '\n'; + static_assert( FieldDelimiter != FieldDelimiter2 ); + + Delimiter< "\n"_cs > RecordDelimiter; } namespace storage { - inline StaticValue< std::optional< std::string > > globalFieldDelimiter; - inline StaticValue< std::optional< char > > globalRecordDelimiter; + template< auto Delim > + inline StaticValue< std::optional< std::string > > globalDelimiter; } - inline std::string - globalFieldDelimiter() + template< auto Delim > + std::string & + globalDelimiter() { - if( not storage::globalFieldDelimiter().has_value() ) storage::globalFieldDelimiter()= C::defaultFieldDelimiter; + if( not storage::globalDelimiter< Delim >().has_value() ) + { + + storage::globalDelimiter()= std::string{} + static_cast< char >( Delim ); + } + assert( storage::globalDelimiter< Delim >().has_value() ); return storage::globalFieldDelimiter().value(); } namespace exports { - inline void - setGlobalFieldDelimiter( const std::string delim ) + template< auto Delim > + void + setGlobaDelimiter( const std::string delim ) { - storage::globalFieldDelimiter()= delim; + storage::globalDelimiter< Delim >()= delim; } } - inline char - globalRecordDelimiter() - { - if( not storage::globalRecordDelimiter().has_value() ) storage::globalRecordDelimiter()= C::defaultRecordDelimiter; - return storage::globalRecordDelimiter().value(); - } + template< auto Delim > + using DelimiterStateByValue= StreamState< Delim, std::string, globalDelimiter< Delim > >; - using FieldDelimiterState= StreamState< decltype( FieldDelimiter ), std::string, globalFieldDelimiter >; + - inline std::ostream & - operator << ( std::ostream &os, decltype( FieldDelimiter ) ) + template< typename DelimType > + using DelimiterState= StreamState< Delim, std::string, globalDelimiter >; + + + struct DelimWrap + { + template< typename Delim > + Delim val; + }; + + std::ostream & + operator << ( std::ostream &os, auto DelimVal ) { - return os << FieldDelimiterState::get( os ); + return os << DelimiterState< decltype( DelimVal ) >::get( os ); } namespace exports @@ -122,7 +168,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters } } -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; } From bb516602727e99918e6b978680a11cce9868db50 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 31 Oct 2023 02:17:27 -0400 Subject: [PATCH 2/2] Simplify the delimiters --- IOStreams/IStreamable.h | 2 +- IOStreams/OStreamable.h | 2 +- IOStreams/OStreamable.test/0.cc | 12 +-- IOStreams/StreamState.h | 39 ++++++--- IOStreams/delimiters.h | 148 +++++--------------------------- IOStreams/streamable.test/0.cc | 6 +- 6 files changed, 64 insertions(+), 145 deletions(-) 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 e538f0e..e2d7e77 100644 --- a/IOStreams/StreamState.h +++ b/IOStreams/StreamState.h @@ -11,7 +11,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state { inline namespace exports { - template< typename Tag, typename Type, auto Default= [] { return Type{}; } > + template< typename > class StreamState; } @@ -20,7 +20,14 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state { private: 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 ) { @@ -30,7 +37,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state Type *& get_ptr( std::ios_base &ios ) { - get_ptr( ios, index ); + return get_ptr( ios, index ); } static void @@ -50,7 +57,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state } else if( event == std::ios_base::copyfmt_event ) { - get_ptr( ios, idx )= new Type{ get( ios, idx ) }; + get_ptr( ios, idx )= new Type{ *get_ptr( ios, idx ) }; } } @@ -63,13 +70,13 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state void init( std::ios_base &ios ) { - if( not ios.iword( idx ) ) + if( not ios.iword( index ) ) { - ios.iword( index() )= 1; - ios.register_callback( callback, idx ); + ios.iword( index )= 1; + ios.register_callback( callback, index ); } - auto *&ptr= get_ptr( ios, idx ); - if( not ptr ) ptr= new Type{ Default() }; + auto *&ptr= get_ptr( ios, index ); + if( not ptr ) ptr= new Type{ build() }; } public: @@ -80,6 +87,12 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state return *get_ptr( ios ); } + void + setDefault( const Type value ) + { + build= [value] { return value; }; + } + struct Setter { StreamState *state; @@ -88,17 +101,23 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state friend std::ostream & operator << ( std::ostream &os, const Setter &s ) { - s.get( os )= s.val; + s.state->get( os )= s.val; return os; } friend std::istream & operator >> ( std::istream &is, const Setter &s ) { - s.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 91c5a08..8cffc76 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -24,146 +24,44 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters_m struct Delimiter : boost::noncopyable { - std::string current; - StreamState state; + StreamState< std::string > state; explicit - Delimiter( const std::string current ) - : current( current ) - { - } + Delimiter( const std::string dflt ) + : state( [dflt] { return dflt; } ) {} }; - template< ConstexprString s > - using CreateDelimiter= void ( const DelimiterParamType< s > & ); - - template< typename T > - constexpr is_delimiter_v= false; - - template< ConstexprString s > - constexpr is_delimiter_v< CreateDelimiter< s > >; - - inline StaticValue< std::map< void (*)( const DelimiterBase & ), StreamState< Delim, std::string, globalDelimiter< - - - template< typename Delimiter > - using DelimiterStateByValue= StreamState< Delim, std::string, globalDelimiter< Delim > >; - - inline namespace exports - { - Delimiter< "\t"_cs > FieldDelimiter; - Delimiter< "\t"_cs > FieldDelimiter2; - - static_assert( FieldDelimiter != FieldDelimiter2 ); - - Delimiter< "\n"_cs > RecordDelimiter; - } - - namespace storage - { - template< auto Delim > - inline StaticValue< std::optional< std::string > > globalDelimiter; - } - - template< auto Delim > - std::string & - globalDelimiter() - { - if( not storage::globalDelimiter< Delim >().has_value() ) - { - - storage::globalDelimiter()= std::string{} + static_cast< char >( Delim ); - } - assert( storage::globalDelimiter< Delim >().has_value() ); - return storage::globalFieldDelimiter().value(); - } - namespace exports { - template< auto Delim > - void - setGlobaDelimiter( const std::string delim ) - { - storage::globalDelimiter< Delim >()= delim; - } - } - - template< auto Delim > - using DelimiterStateByValue= StreamState< Delim, std::string, globalDelimiter< Delim > >; - - - - template< typename DelimType > - using DelimiterState= StreamState< Delim, std::string, globalDelimiter >; - - - struct DelimWrap - { - template< typename Delim > - Delim val; - }; - - std::ostream & - operator << ( std::ostream &os, auto DelimVal ) - { - return os << DelimiterState< decltype( DelimVal ) >::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 Delimiter fieldDelimiter{ "\t" }; + inline Delimiter recordDelimiter{ "\n" }; } inline std::ostream & - operator << ( std::ostream &os, decltype( RecordDelimiter ) ) + operator << ( std::ostream &os, Delimiter &delim ) { - return os << getRecordDelimiter( os ); + const auto &s= delim.state.get( os ); + return os << s; } - struct RecordDelimiterSetter - { - const char ch; - - friend std::ostream & - operator << ( std::ostream &os, const RecordDelimiterSetter &s ) - { - setRecordDelimiterOnIOS( os, s.ch ); - return os; - } - }; - namespace exports { - auto - setRecordDelimiter( const char ch ) + 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 ); } } } 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(); }