From 7553289283091e89db7aafa402211e84834b23dd Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 03:32:37 -0400 Subject: [PATCH 01/11] Move concepts to Hydrogen ABI. --- Concepts.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Concepts.h b/Concepts.h index ef48216..0f36a96 100644 --- a/Concepts.h +++ b/Concepts.h @@ -2,6 +2,8 @@ static_assert( __cplusplus > 2020'00 ); #pragma once +#include + #include #include #include @@ -9,7 +11,7 @@ static_assert( __cplusplus > 2020'00 ); #include "meta.h" #include "function_traits.h" -namespace Alepha::inline Cavorite ::detail:: core_concepts +namespace Alepha::Hydrogen ::detail:: core_concepts { inline namespace exports { @@ -278,7 +280,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts } } -namespace Alepha::Cavorite::inline exports::inline core_concepts +namespace Alepha::Hydrogen::inline exports::inline core_concepts { using namespace detail::core_concepts::exports; } From c66282de821eb1da69ce7053e33195f3c46be481 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 03:33:02 -0400 Subject: [PATCH 02/11] Move base meta helpers to Hydrogen ABI These probably should be consolidated into `Alepha::Meta`, but for now, this should be fine. --- meta.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta.h b/meta.h index 2e9ca92..0162c6d 100644 --- a/meta.h +++ b/meta.h @@ -16,7 +16,7 @@ static_assert( __cplusplus > 2020'00 ); #include "type_lisp.h" -namespace Alepha::inline Cavorite ::detail:: meta +namespace Alepha::Hydrogen ::detail:: meta { inline namespace exports { @@ -71,7 +71,7 @@ namespace Alepha::inline Cavorite ::detail:: meta } } -namespace Alepha::inline Cavorite::inline exports::inline meta +namespace Alepha::Hydrogen::inline exports::inline meta { using namespace detail::meta::exports; } From 2bbaa65d6851ba3def027ffe6dc88c4a7b259775 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 03:34:25 -0400 Subject: [PATCH 03/11] Move function_traits into Hydrogen ABI. --- function_traits.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/function_traits.h b/function_traits.h index 878be83..d64a42e 100644 --- a/function_traits.h +++ b/function_traits.h @@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 ); #include #include -namespace Alepha::inline Cavorite ::detail:: function_traits_module +namespace Alepha::Hydrogen ::detail:: function_traits_module { inline namespace exports { @@ -76,7 +76,7 @@ namespace Alepha::inline Cavorite ::detail:: function_traits_module {}; } -namespace Alepha::Cavorite::inline exports::inline function_traits_module +namespace Alepha::Hydrogen::inline exports::inline function_traits_module { using namespace detail::function_traits_module::exports; } From 5e86b58987a4bc114d70ff5b77554e70c4e397d6 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 03:40:57 -0400 Subject: [PATCH 04/11] Start to migrate delimiter code to common helper. --- IOStreams/OStreamable.test/0.cc | 8 ++- IOStreams/StreamState.h | 101 ++++++++++++++++++++++++++++++++ IOStreams/delimiters.h | 45 ++++---------- 3 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 IOStreams/StreamState.h 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 }; } } From bacaa9fcee300f388054bbb0360e5d98acf41acb Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 03:55:42 -0400 Subject: [PATCH 05/11] Make stream state have a default-construction option. Start integrating it for the field delimiters. --- IOStreams/OStreamable.test/0.cc | 36 ++++++++++++++++++++++++++++++++- IOStreams/StreamState.h | 6 +++--- IOStreams/delimiters.h | 13 ++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/IOStreams/OStreamable.test/0.cc b/IOStreams/OStreamable.test/0.cc index 75280cd..21486b0 100644 --- a/IOStreams/OStreamable.test/0.cc +++ b/IOStreams/OStreamable.test/0.cc @@ -30,10 +30,28 @@ namespace stringify( const Agg &agg, const std::string delim ) { std::ostringstream oss; + Alepha::IOStreams::setGlobalFieldDelimiter( "YOU SHOULD NOT SEE THIS" ); oss << Alepha::IOStreams::setFieldDelimiter( delim ); oss << agg; return std::move( oss ).str(); } + + auto + stringify_global( const Agg &agg, const std::string delim ) + { + std::ostringstream oss; + Alepha::IOStreams::setGlobalFieldDelimiter( delim ); + oss << agg; + return std::move( oss ).str(); + } + + auto + stringify_default( const Agg &agg ) + { + std::ostringstream oss; + oss << agg; + return std::move( oss ).str(); + } } static auto init= Alepha::Utility::enroll <=[] @@ -41,7 +59,13 @@ static auto init= Alepha::Utility::enroll <=[] using namespace Alepha::Testing::exports; using namespace Alepha::Testing::literals::test_literals; - "Simple OStream"_test <=TableTest< stringify > + "Simple OStream (default delimiter)"_test <=TableTest< stringify_default > + ::Cases + { + { "smoke test", { { 1, 2, 3 } }, { "1\t2\t3" } }, + }; + + "Simple OStream (specific delimiter)"_test <=TableTest< stringify > ::Cases { { "smoke test", { { 1, 2, 3 }, "\t" }, { "1\t2\t3" } }, @@ -49,4 +73,14 @@ static auto init= Alepha::Utility::enroll <=[] { "smoke test", { { 1, 2, 3 }, ";;" }, { "1;;2;;3" } }, { "smoke test", { { 1, 2, 3 }, ", " }, { "1, 2, 3" } }, }; + + "Simple OStream (global delimiter)"_test <=TableTest< stringify_global > + ::Cases + { + { "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 index 0f32f40..dc23936 100644 --- a/IOStreams/StreamState.h +++ b/IOStreams/StreamState.h @@ -11,11 +11,11 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state { inline namespace exports { - template< typename Tag, typename Type > + template< typename Tag, typename Type, auto Default= [] { return Type{}; } > class StreamState; } - template< typename Tag, typename Type > + template< typename Tag, typename Type, auto Default > class exports::StreamState { private: @@ -70,7 +70,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: stream_state ios.register_callback( callback, index() ); } auto *&ptr= get_ptr( ios ); - if( not ptr ) ptr= new Type{}; + if( not ptr ) ptr= new Type{ Default() }; } public: diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h index 097f4cd..6f543e3 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -20,8 +20,6 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters enum { RecordDelimiter }; } - using FieldDelimiterState= StreamState< decltype( FieldDelimiter ), std::string >; - namespace C { const std::string defaultFieldDelimiter= "\t"; @@ -41,6 +39,15 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters return storage::globalFieldDelimiter().value(); } + namespace exports + { + inline void + setGlobalFieldDelimiter( const std::string delim ) + { + storage::globalFieldDelimiter()= delim; + } + } + inline char globalRecordDelimiter() { @@ -48,6 +55,8 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters return storage::globalRecordDelimiter().value(); } + using FieldDelimiterState= StreamState< decltype( FieldDelimiter ), std::string, globalFieldDelimiter >; + inline std::ostream & operator << ( std::ostream &os, decltype( FieldDelimiter ) ) { From c2bc8dae2eced94e8c1103218b052dace48bce55 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 04:03:39 -0400 Subject: [PATCH 06/11] Stream builder notation for strings. This should be less boilerplate than repeated `lexical_cast` or `stringify` calls when building strings. --- IOStreams/OStreamable.test/0.cc | 23 +++++++++++-- IOStreams/Stream.h | 61 +++++++++++++++++++++++++++++++++ ProgramOptions.h | 5 +-- Testing/TableTest.h | 8 +++-- tuplize_args.h | 14 ++++---- 5 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 IOStreams/Stream.h diff --git a/IOStreams/OStreamable.test/0.cc b/IOStreams/OStreamable.test/0.cc index 21486b0..bb962e2 100644 --- a/IOStreams/OStreamable.test/0.cc +++ b/IOStreams/OStreamable.test/0.cc @@ -10,6 +10,7 @@ static_assert( __cplusplus > 2020'00 ); #include #include +#include namespace { @@ -27,7 +28,7 @@ namespace auto - stringify( const Agg &agg, const std::string delim ) + stringify_specific( const Agg &agg, const std::string delim ) { std::ostringstream oss; Alepha::IOStreams::setGlobalFieldDelimiter( "YOU SHOULD NOT SEE THIS" ); @@ -58,6 +59,7 @@ static auto init= Alepha::Utility::enroll <=[] { using namespace Alepha::Testing::exports; using namespace Alepha::Testing::literals::test_literals; + using namespace Alepha::IOStreams::exports::stream; "Simple OStream (default delimiter)"_test <=TableTest< stringify_default > ::Cases @@ -65,7 +67,24 @@ static auto init= Alepha::Utility::enroll <=[] { "smoke test", { { 1, 2, 3 } }, { "1\t2\t3" } }, }; - "Simple OStream (specific delimiter)"_test <=TableTest< stringify > + "Simple OStream (specific delimiter)"_test <=TableTest< stringify_specific > + ::Cases + { + { "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" } }, + }; + + "Simple OStream (stream builder)"_test <=TableTest + < + []( const Agg agg, const std::string delim ) + { + using Alepha::IOStreams::Stream; + using Alepha::IOStreams::setFieldDelimiter; + return Stream{} << setFieldDelimiter( delim ) << agg << FinishString; + } + > ::Cases { { "smoke test", { { 1, 2, 3 }, "\t" }, { "1\t2\t3" } }, diff --git a/IOStreams/Stream.h b/IOStreams/Stream.h new file mode 100644 index 0000000..d5f0026 --- /dev/null +++ b/IOStreams/Stream.h @@ -0,0 +1,61 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include + +#include + +namespace Alepha::Hydrogen::IOStreams ::detail:: stream +{ + inline namespace exports + { + class Stream; + + enum { FinishString }; + + std::string stringify( const Alepha::OStreamable auto &item, Alepha::OStreamable auto && ... params ); + } + + class exports::Stream + { + private: + // TODO: We need the exception throwing capabilities of the + // `boost::lexical_cast` operation. But this stream technique + // lets us build strings using stream modifiers and manipulators, + // which `boost::lexical_cast` doesn't support. + std::ostringstream oss; + + public: + Stream && + operator << ( const Alepha::OStreamable auto &t ) && + { + oss << t; + return std::move( *this ); + } + + std::string + operator << ( decltype( FinishString ) ) && + { + return std::move( oss ).str(); + } + + operator std::string () && + { + return std::move( *this ) << FinishString; + } + }; + + inline std::string + exports::stringify( const Alepha::OStreamable auto &item, Alepha::OStreamable auto && ... params ) + { + return ( Stream{} << ... << params ) << item << FinishString; + } +} + +namespace Alepha::Hydrogen::IOStreams::inline exports::inline stream +{ + using namespace detail::stream::exports; +} diff --git a/ProgramOptions.h b/ProgramOptions.h index 23a5b0b..de678f7 100644 --- a/ProgramOptions.h +++ b/ProgramOptions.h @@ -89,10 +89,11 @@ static_assert( __cplusplus > 2020'00 ); #include -#include #include #include +#include + #include namespace Alepha::inline Cavorite ::detail:: program_options @@ -265,7 +266,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options // variable's value in C++ at runtime. auto defaultBuilder= [&value] { - auto text= boost::lexical_cast< std::string >( value ); + auto text= IOStreams::stringify( value ); if( text.find_first_of( " \n\t" ) != std::string::npos ) { diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 942deaa..890418d 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -29,6 +29,8 @@ static_assert( __cplusplus > 2020'00 ); #include #include +#include + #include #include @@ -122,7 +124,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test } else if constexpr( Meta::is_ostreamable_v< T > ) { - return boost::lexical_cast< std::string >( v ); + return IOStreams::stringify( v ); } else if constexpr( Meta::is_optional_v< T > ) { @@ -269,11 +271,11 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test const auto result= witness == expected; if( not result ) { - std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; ++failureCount; printDebugging< outputMode >( witness, expected ); } - else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; } return failureCount; diff --git a/tuplize_args.h b/tuplize_args.h index b01d3c9..704e0b7 100644 --- a/tuplize_args.h +++ b/tuplize_args.h @@ -12,10 +12,12 @@ static_assert( __cplusplus > 2020'00 ); #include #include #include -#include +#include #include +#include + #include "meta.h" #include "error.h" #include "Concepts.h" @@ -48,10 +50,10 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args explicit ArityMismatchError( const std::size_t remaining, const std::size_t processed, const std::string &clarification= "" ) : remaining_( remaining ), processed_( processed ), clarification( clarification ), - message( ( clarification.empty() ? "" : ( clarification + ": " ) ) - + "Argument count mismatch. " - + boost::lexical_cast< std::string >( remaining ) + " remaining " - + boost::lexical_cast< std::string >( processed ) + " processed" ) {} + message( IOStream::Stream{} << ( clarification.empty() ? "" : ( clarification + ": " ) ) + << "Argument count mismatch. " + << remaining << " remaining " + << processed j< " processed" ) {} const char * @@ -109,7 +111,7 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args const std::vector< std::string > rv; std::transform( begin( args ) + offset, end( args ), back_inserter( rv ), - boost::lexical_cast< type, std::string > ); + IOStreams::stringify< type > ); return std::tuple_cat( std::tuple{ arv }, tuplizeArgsBackend( tail{}, args, offset + rv.size() ) ); } else From 6bad057af3d22b3bcb86e9b80c3a26157ba5e27a Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 04:19:05 -0400 Subject: [PATCH 07/11] Migrate `type_lisp` into Hydrogen ABI. --- type_lisp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/type_lisp.h b/type_lisp.h index bd0bfb1..ce71f4f 100644 --- a/type_lisp.h +++ b/type_lisp.h @@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'00 ); #include -namespace Alepha::inline Cavorite ::detail:: type_lisp +namespace Alepha::Hydrogen ::detail:: type_lisp { inline namespace exports {} @@ -85,7 +85,7 @@ namespace Alepha::inline Cavorite ::detail:: type_lisp } } -namespace Alepha::Cavorite::inline exports::inline type_lisp +namespace Alepha::Hydrogen::inline exports::inline type_lisp { using namespace detail::type_lisp::exports; } From 0fb0b578d34d48fff2a03dbcafb9c2373cf5da29 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 04:46:54 -0400 Subject: [PATCH 08/11] Discard unit test library explicit links. --- IOStreams/IStreamable.test/CMakeLists.txt | 1 - IOStreams/OStreamable.test/CMakeLists.txt | 2 -- Utility/StackableStreambuf.test/CMakeLists.txt | 1 - string_algorithms.test/CMakeLists.txt | 2 -- word_wrap.test/CMakeLists.txt | 1 - 5 files changed, 7 deletions(-) diff --git a/IOStreams/IStreamable.test/CMakeLists.txt b/IOStreams/IStreamable.test/CMakeLists.txt index d31c4a6..b099603 100644 --- a/IOStreams/IStreamable.test/CMakeLists.txt +++ b/IOStreams/IStreamable.test/CMakeLists.txt @@ -1,2 +1 @@ -link_libraries( unit-test ) unit_test( 0 ) diff --git a/IOStreams/OStreamable.test/CMakeLists.txt b/IOStreams/OStreamable.test/CMakeLists.txt index 8ef1b19..b099603 100644 --- a/IOStreams/OStreamable.test/CMakeLists.txt +++ b/IOStreams/OStreamable.test/CMakeLists.txt @@ -1,3 +1 @@ -link_libraries( unit-test ) - unit_test( 0 ) diff --git a/Utility/StackableStreambuf.test/CMakeLists.txt b/Utility/StackableStreambuf.test/CMakeLists.txt index d31c4a6..b099603 100644 --- a/Utility/StackableStreambuf.test/CMakeLists.txt +++ b/Utility/StackableStreambuf.test/CMakeLists.txt @@ -1,2 +1 @@ -link_libraries( unit-test ) unit_test( 0 ) diff --git a/string_algorithms.test/CMakeLists.txt b/string_algorithms.test/CMakeLists.txt index 8ef1b19..b099603 100644 --- a/string_algorithms.test/CMakeLists.txt +++ b/string_algorithms.test/CMakeLists.txt @@ -1,3 +1 @@ -link_libraries( unit-test ) - unit_test( 0 ) diff --git a/word_wrap.test/CMakeLists.txt b/word_wrap.test/CMakeLists.txt index d31c4a6..b099603 100644 --- a/word_wrap.test/CMakeLists.txt +++ b/word_wrap.test/CMakeLists.txt @@ -1,2 +1 @@ -link_libraries( unit-test ) unit_test( 0 ) From 5908f8d1267c60b36a292473401405dd828ceadb Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 04:47:23 -0400 Subject: [PATCH 09/11] Add a bit more support for tuple/type-lisp crossover. --- type_lisp.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/type_lisp.h b/type_lisp.h index ce71f4f..94d0134 100644 --- a/type_lisp.h +++ b/type_lisp.h @@ -83,6 +83,36 @@ namespace Alepha::Hydrogen ::detail:: type_lisp template< typename Element > constexpr bool list_contains_v< Nil, Element >{}; } + + template< typename Element, typename List > + struct cons; + + template< typename Element, typename ... Members > + struct cons< Element, TypeList< Members... > > + { + using type= TypeList< Element, Members... >; + }; + + namespace exports + { + template< typename Element, typename List > + using cons_t= typename cons< Element, List >::type; + } + + template< typename Tuple > + struct list_from_tuple; + + namespace exports + { + template< typename Tuple > + using list_from_tuple_t= typename list_from_tuple< Tuple >::type; + } + + template< typename ... Members > + struct list_from_tuple< std::tuple< Members... > > + { + using type= TypeList< Members... >; + }; } namespace Alepha::Hydrogen::inline exports::inline type_lisp From 89dd3cc81c36e399cc3c3ce8f33e9242503aa64b Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 04:48:09 -0400 Subject: [PATCH 10/11] Tuplize args testing. The code bit-rotted a bit. Some internals of Alepha changed, this impl of tuplize args being imported from a near-dead laptop. --- CMakeLists.txt | 1 + tuplize_args.h | 21 +++++++-------- tuplize_args.test/0.cc | 44 ++++++++++++++++++++++++++++++++ tuplize_args.test/CMakeLists.txt | 1 + 4 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 tuplize_args.test/0.cc create mode 100644 tuplize_args.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ce4949..f28bb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory( comparisons.test ) add_subdirectory( Exception.test ) add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) +add_subdirectory( tuplize_args.test ) # Sample applications add_executable( example example.cc ) diff --git a/tuplize_args.h b/tuplize_args.h index 704e0b7..0cb729c 100644 --- a/tuplize_args.h +++ b/tuplize_args.h @@ -33,7 +33,7 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args * * Iterates thru the runtime arguments and attempts to parse them to the compiletime specified types. */ - template< typename Tuple T > + template< Tuple T > auto tuplizeArgs( const std::vector< std::string > &args ); @@ -50,10 +50,10 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args explicit ArityMismatchError( const std::size_t remaining, const std::size_t processed, const std::string &clarification= "" ) : remaining_( remaining ), processed_( processed ), clarification( clarification ), - message( IOStream::Stream{} << ( clarification.empty() ? "" : ( clarification + ": " ) ) + message( IOStreams::Stream{} << ( clarification.empty() ? "" : ( clarification + ": " ) ) << "Argument count mismatch. " << remaining << " remaining " - << processed j< " processed" ) {} + << processed << " processed" ) {} const char * @@ -74,7 +74,7 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args // TODO: Expand this to handle compiletime-bound defaulted values. template< typename Type > - concept Omittable= Optional< Type >; + concept Omittable= is_optional_v< Type >; template< TypeListType list > tuple_from_list_t< list > @@ -101,18 +101,19 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args else return tuplizeArgsBackend( cons_t< typename first::value_type, tail >{}, args, offset ); } else if( offset >= args.size() ) throw ArityMismatchError{ args.size() - offset, offset, "no more arguments left" }; - else if constexpr( is_vector_v< first > ) + else if constexpr( Vector< first > ) { static_assert( std::is_same_v< cdr_t< list >, Nil >, "A vector is only permissible as the final argument." ); if( args.size() <= offset ) { - throw ArityMismatchError{ args.size() - offset, offset, "a vector/list requires at least one runtime argument." } + throw ArityMismatchError{ args.size() - offset, offset, "a vector/list requires at least one runtime argument." }; } + using first_type= typename first::value_type; - const std::vector< std::string > rv; + std::vector< first_type > rv; std::transform( begin( args ) + offset, end( args ), back_inserter( rv ), - IOStreams::stringify< type > ); - return std::tuple_cat( std::tuple{ arv }, tuplizeArgsBackend( tail{}, args, offset + rv.size() ) ); + boost::lexical_cast< first_type, std::string > ); + return std::tuple_cat( std::tuple{ rv }, tuplizeArgsBackend( tail{}, args, offset + rv.size() ) ); } else { @@ -131,7 +132,7 @@ namespace Alepha::Hydrogen ::detail:: tuplize_args auto exports::tuplizeArgs( const std::vector< std::string > &args ) { - return tuplizeArgsBackend( list_from_tuple_t< T > args ); + return tuplizeArgsBackend( list_from_tuple_t< T >{}, args ); } } diff --git a/tuplize_args.test/0.cc b/tuplize_args.test/0.cc new file mode 100644 index 0000000..d5d5cb1 --- /dev/null +++ b/tuplize_args.test/0.cc @@ -0,0 +1,44 @@ +#include "../tuplize_args.h" + +#include +#include + +#include + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::exports; + using namespace Alepha::Utility::exports::evaluation_helpers; + + "A basic tuplization example"_test <=TableTest + < + Alepha::tuplizeArgs< std::tuple< int, std::string, int, char > > + > + ::Cases + { + { "Smoke example", { { "1", "Hello", "42", "x" } }, { 1, "Hello", 42, 'x' } }, + }; + + "Do trailing vectors permit variadics?"_test <=TableTest + < + Alepha::tuplizeArgs< std::tuple< int, std::string, std::vector< int > > > + > + ::Cases + { + { "One extra argument", { { "1", "Hello", "2" } }, { 1, "Hello", { 2 } } }, + { "Two extra arguments", { { "1", "Hello", "2", "3" } }, { 1, "Hello", { 2, 3 } } }, + { "Three extra arguments", { { "1", "Hello", "2", "3", "4" } }, { 1, "Hello", { 2, 3, 4 } } }, + }; + + "Does trailing optional stacked permit variadics??"_test <=TableTest + < + Alepha::tuplizeArgs< std::tuple< std::optional< int >, std::optional< std::string >, std::optional< int > > > + > + ::Cases + { + { "No arguments", { {} }, { std::nullopt, std::nullopt, std::nullopt } }, + { "One argument", { { "1" } }, { 1, std::nullopt, std::nullopt } }, + { "Two arguments", { { "1", "Hello" } }, { 1, "Hello", std::nullopt } }, + { "Three arguments", { { "1", "Hello", "2" } }, { 1, "Hello", 2 } }, + }; +}; diff --git a/tuplize_args.test/CMakeLists.txt b/tuplize_args.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/tuplize_args.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From fe305f52c38599b8de6e13bb67606cb5a20d24b7 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 25 Oct 2023 05:02:21 -0400 Subject: [PATCH 11/11] Debug mode should build unoptimized, to help with backtraces. --- cmake/rules.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/rules.cmake b/cmake/rules.cmake index e9a5d8f..ccb603e 100644 --- a/cmake/rules.cmake +++ b/cmake/rules.cmake @@ -5,6 +5,10 @@ add_compile_options( -fdiagnostics-column-unit=byte ) add_compile_options( -fconcepts-diagnostics-depth=0 ) include_directories( ${CMAKE_BINARY_DIR} . ) +if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" ) +add_compile_options( -O0 ) +add_compile_options( -g ) +endif() list( APPEND CMAKE_CTEST_ARGUMENTS "-VV" )