From 807311e0dde16a94e946101dcbe83355d08c05b0 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sat, 15 Jun 2024 17:41:13 -0400 Subject: [PATCH 1/4] In this file, `Concepts.h` should be non-relative --- template_for_each.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template_for_each.h b/template_for_each.h index 79822da..1ec5d61 100644 --- a/template_for_each.h +++ b/template_for_each.h @@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'99 ); #include -#include "Concepts.h" +#include namespace Alepha::Hydrogen ::detail:: template_for_each_m { From b5fb8c76f2dcb5d4965b3dd4eeb2c6b3344125e6 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 5 Jul 2024 11:54:01 -0400 Subject: [PATCH 2/4] Create C++26(?) emulation of `template for`. --- CMakeLists.txt | 1 + template_for_each.h | 27 +++++++++++++++++ template_for_each.test/0.cc | 42 +++++++++++++++++++++++++++ template_for_each.test/CMakeLists.txt | 1 + 4 files changed, 71 insertions(+) create mode 100644 template_for_each.test/0.cc create mode 100644 template_for_each.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index b9f4118..2a95f4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_subdirectory( comparisons.test ) add_subdirectory( Exception.test ) add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) +add_subdirectory( template_for_each.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) diff --git a/template_for_each.h b/template_for_each.h index 1ec5d61..a3a30a7 100644 --- a/template_for_each.h +++ b/template_for_each.h @@ -8,6 +8,8 @@ static_assert( __cplusplus > 2020'99 ); #include +#include + namespace Alepha::Hydrogen ::detail:: template_for_each_m { inline namespace exports @@ -79,6 +81,31 @@ namespace Alepha::Hydrogen ::detail:: template_for_each_m { return for_each_syntax_adaptor< const Tuple >{ tuple }; } + + template< typename Type > + concept TemplateLoopable= false + or Tuple< Type > + or Aggregate< Type > + // or Array< T > // Do we need array support? Does it matter? + ; + + template< TemplateLoopable Type > + [[nodiscard]] + constexpr auto + template_for( Type &tuple, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept + { + tupled= Reflection::tuplizeAggregate( tuple ); + return tuple_for_each( *tupled ); + } + + template< TemplateLoopable Type > + [[nodiscard]] + constexpr auto + template_for( const Type &tuple, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept + { + tupled= Reflection::tuplizeAggregate( tuple ); + return tuple_for_each( *tupled ); + } } } diff --git a/template_for_each.test/0.cc b/template_for_each.test/0.cc new file mode 100644 index 0000000..628f23a --- /dev/null +++ b/template_for_each.test/0.cc @@ -0,0 +1,42 @@ +static_assert( __cplusplus > 2023'00 ); + +#include "../template_for_each.h" + +#include +#include + +#include + +#include + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::literals; + using namespace Alepha::Testing::exports; + + struct Example + { + int x; + std::string s; + int y; + }; + + "Can one `template_for` each over a simple struct and get the members into a string?"_test <=TableTest + < + []( Example ex ) + { + using Alepha::template_for; + std::ostringstream oss; + template_for( ex ) <=[&]( const auto &element ) + { + oss << element << std::endl; + }; + + return oss.str(); + } + > + ::Cases + { + { "Simple case", { { 42, "Hello World", 1138 } }, "42\nHello World\n1138\n" }, + }; +}; diff --git a/template_for_each.test/CMakeLists.txt b/template_for_each.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/template_for_each.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From 32e6c3657047ff88d78939908b5a19487cb3af44 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 5 Jul 2024 11:59:26 -0400 Subject: [PATCH 3/4] Rename the `template_for` header. C++26 (I hope) is supposed to have this syntax: ``` template for( const auto &element: aggregate ) { ...; } ``` Thus, I've adjusted this gadget to have a similar name, to enable simple mechanical code changes. From Alepha, you'd use it thus: ``` template_for( aggregate ) <=[&]( const auto &element ) { ...; }; ``` --- CMakeLists.txt | 2 +- IOStreams/IStreamable.h | 2 +- IOStreams/OStreamable.h | 2 +- Testing/TableTest.h | 2 +- template_for_each.h => template_for.h | 0 {template_for_each.test => template_for.test}/0.cc | 2 +- {template_for_each.test => template_for.test}/CMakeLists.txt | 0 7 files changed, 5 insertions(+), 5 deletions(-) rename template_for_each.h => template_for.h (100%) rename {template_for_each.test => template_for.test}/0.cc (95%) rename {template_for_each.test => template_for.test}/CMakeLists.txt (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a95f4f..a711112 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ add_subdirectory( comparisons.test ) add_subdirectory( Exception.test ) add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) -add_subdirectory( template_for_each.test ) +add_subdirectory( template_for.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) diff --git a/IOStreams/IStreamable.h b/IOStreams/IStreamable.h index f80f449..8799ad9 100644 --- a/IOStreams/IStreamable.h +++ b/IOStreams/IStreamable.h @@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include #include #include diff --git a/IOStreams/OStreamable.h b/IOStreams/OStreamable.h index 87e5c3b..9b11696 100644 --- a/IOStreams/OStreamable.h +++ b/IOStreams/OStreamable.h @@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include #include #include #include diff --git a/Testing/TableTest.h b/Testing/TableTest.h index b956b1e..eb82680 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -16,7 +16,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include #include diff --git a/template_for_each.h b/template_for.h similarity index 100% rename from template_for_each.h rename to template_for.h diff --git a/template_for_each.test/0.cc b/template_for.test/0.cc similarity index 95% rename from template_for_each.test/0.cc rename to template_for.test/0.cc index 628f23a..ecffee0 100644 --- a/template_for_each.test/0.cc +++ b/template_for.test/0.cc @@ -1,6 +1,6 @@ static_assert( __cplusplus > 2023'00 ); -#include "../template_for_each.h" +#include "../template_for.h" #include #include diff --git a/template_for_each.test/CMakeLists.txt b/template_for.test/CMakeLists.txt similarity index 100% rename from template_for_each.test/CMakeLists.txt rename to template_for.test/CMakeLists.txt From 970cfa3b627e5ef6eccc4b2894169a95175414c2 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 5 Jul 2024 12:18:18 -0400 Subject: [PATCH 4/4] Unify everything to `template_for` form. --- IOStreams/IStreamable.h | 2 +- IOStreams/OStreamable.h | 2 +- Testing/TableTest.h | 2 +- Testing/printDebugging.h | 2 +- template_for.h | 169 ++++++++++++++++++++------------------- 5 files changed, 91 insertions(+), 86 deletions(-) diff --git a/IOStreams/IStreamable.h b/IOStreams/IStreamable.h index 8799ad9..da2bb5b 100644 --- a/IOStreams/IStreamable.h +++ b/IOStreams/IStreamable.h @@ -51,7 +51,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: IStreamable_m int index= 0; // TODO: Consider the lens system here... but the basic use case seems to be for // aggregates, so we'll go with this simple case for now... - tuple_for_each( decomposed ) <=[&]( auto &element ) + template_for( decomposed ) <=[&]( auto &element ) { std::istringstream iss{ tokens.at( index++ ) }; iss >> element; diff --git a/IOStreams/OStreamable.h b/IOStreams/OStreamable.h index 9b11696..b8e6e5b 100644 --- a/IOStreams/OStreamable.h +++ b/IOStreams/OStreamable.h @@ -45,7 +45,7 @@ namespace Alepha::Hydrogen::IOStreams ::detail:: OStreamable_m // TODO: Consider the lens system here... but the basic use case seems to be for // aggregates, so we'll go with this simple case for now... - tuple_for_each( decomposed ) <=[&]( const auto &element ) + template_for( decomposed ) <=[&]( const auto &element ) { os << NextItem << element; }; diff --git a/Testing/TableTest.h b/Testing/TableTest.h index eb82680..3ddf878 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -206,7 +206,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m oss << std::endl << "Test inputs were: " << std::endl; int index= 0; - tuple_for_each( params ) <=[&]( const auto ¶m ) + template_for( params ) <=[&]( const auto ¶m ) { // Debugging output for test inputs is relaxed, as it's not required they be streamable? oss << "Argument " << index++ << ": " << streamAdaptValue< OutputMode::Relaxed >( param ) << std::endl; diff --git a/Testing/printDebugging.h b/Testing/printDebugging.h index 7e0229f..11786e6 100644 --- a/Testing/printDebugging.h +++ b/Testing/printDebugging.h @@ -108,7 +108,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m else if constexpr( Meta::is_tuple_v< T > ) { oss << '['; - tuple_for_each( v ) <=[&oss, first= true]( const auto &elem ) mutable + template_for( v ) <=[&oss, first= true]( const auto &elem ) mutable { if( not first ) oss << ", "; first= false; diff --git a/template_for.h b/template_for.h index a3a30a7..a398107 100644 --- a/template_for.h +++ b/template_for.h @@ -14,98 +14,103 @@ namespace Alepha::Hydrogen ::detail:: template_for_each_m { inline namespace exports { - constexpr void tuple_for_each( const std::tuple<> &, const Functional auto ) noexcept {} + template< Tuple Type > + [[nodiscard]] + constexpr auto + template_for( Type &tuple ) noexcept; - template< typename ... Args, typename Function > + template< Tuple Type > + [[nodiscard]] + constexpr auto + template_for( const Type &tuple ) noexcept; + + template< Aggregate Type > + [[nodiscard]] + constexpr auto + template_for( Type &agg, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept + { + tupled= Reflection::tuplizeAggregate( agg ); + return template_for( *tupled ); + } + + template< Aggregate Type > + [[nodiscard]] + constexpr auto + template_for( const Type &agg, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept + { + tupled= Reflection::tuplizeAggregate( agg ); + return template_for( *tupled ); + } + } + + template< typename ... Args, typename Function > + constexpr void + template_for_impl( const std::tuple< Args... > &tuple, Function body ) + noexcept + ( + ( ... and noexcept( body( std::declval< const Args & >() ) ) ) + ) + { + const auto callWrapper= [&body]( auto &&element ) { body( element ); return nullptr; }; + + auto loop_body_handler= [&]( auto &&... tuple_elements ) + { + std::nullptr_t expansion[]= { callWrapper( tuple_elements )... }; + + std::ignore= expansion; + }; + + std::apply( loop_body_handler, tuple ); + } + + + // Nicer for-each syntax helper: + template< typename Tuple > + struct [[nodiscard]] syntax_adaptor + { + Tuple &tuple; + + template< typename Function > constexpr void - tuple_for_each( const std::tuple< Args... > &tuple, Function body ) - noexcept - ( - ( ... and noexcept( body( std::declval< const Args & >() ) ) ) - ) + operator <= ( Function &&func ) noexcept( noexcept( template_for_impl( tuple, std::forward< Function >( func ) ) ) ) { - const auto callWrapper= [&body]( auto &&element ) { body( element ); return nullptr; }; - - auto loop_body_handler= [&]( auto &&... tuple_elements ) - { - std::nullptr_t expansion[]= { callWrapper( tuple_elements )... }; - - std::ignore= expansion; - }; - - std::apply( loop_body_handler, tuple ); + return template_for_impl( tuple, std::forward< Function >( func ) ); } - // Apply type_identity to all tuple elements - template< typename > struct type_identify_tuple; + constexpr operator decltype( std::ignore ) () const= delete; + }; - template< typename T > - using type_identify_tuple_t= typename type_identify_tuple< T >::type; + template< typename Tuple > + [[nodiscard]] + constexpr auto + template_for_impl( Tuple &tuple ) noexcept + { + return syntax_adaptor< Tuple >{ tuple }; + } - template<> struct type_identify_tuple< std::tuple<> > { using type= std::tuple<>; }; + template< typename Tuple > + [[nodiscard]] + constexpr auto + template_for_impl( const Tuple &tuple ) noexcept + { + return syntax_adaptor< const Tuple >{ tuple }; + } - template< typename ... Args > - struct type_identify_tuple< std::tuple< Args... > > - { - using type= std::tuple< std::type_identity< Args >... >; - }; - // Nicer for-each syntax helper: - template< typename Tuple > - struct for_each_syntax_adaptor - { - Tuple &tuple; + template< Tuple Type > + [[nodiscard]] + constexpr auto + exports::template_for( Type &tuple ) noexcept + { + return template_for_impl( tuple ); + } - template< typename Function > - constexpr void - operator <= ( Function &&func ) noexcept( noexcept( tuple_for_each( tuple, std::forward< Function >( func ) ) ) ) - { - return tuple_for_each( tuple, std::forward< Function >( func ) ); - } - - constexpr operator decltype( std::ignore ) () const= delete; - }; - - template< typename Tuple > - [[nodiscard]] - constexpr auto - tuple_for_each( Tuple &tuple ) noexcept - { - return for_each_syntax_adaptor< Tuple >{ tuple }; - } - - template< typename Tuple > - [[nodiscard]] - constexpr auto - tuple_for_each( const Tuple &tuple ) noexcept - { - return for_each_syntax_adaptor< const Tuple >{ tuple }; - } - - template< typename Type > - concept TemplateLoopable= false - or Tuple< Type > - or Aggregate< Type > - // or Array< T > // Do we need array support? Does it matter? - ; - - template< TemplateLoopable Type > - [[nodiscard]] - constexpr auto - template_for( Type &tuple, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept - { - tupled= Reflection::tuplizeAggregate( tuple ); - return tuple_for_each( *tupled ); - } - - template< TemplateLoopable Type > - [[nodiscard]] - constexpr auto - template_for( const Type &tuple, std::optional< Reflection::aggregate_tuple_t< Type > > &&tupled= {} ) noexcept - { - tupled= Reflection::tuplizeAggregate( tuple ); - return tuple_for_each( *tupled ); - } + template< Tuple Type > + [[nodiscard]] + constexpr auto + exports::template_for( const Type &tuple ) noexcept + { + return template_for_impl( tuple ); } }