diff --git a/CMakeLists.txt b/CMakeLists.txt index 890f2ba..cd8d722 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.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..da2bb5b 100644 --- a/IOStreams/IStreamable.h +++ b/IOStreams/IStreamable.h @@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include #include #include @@ -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 87e5c3b..b8e6e5b 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 @@ -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 b956b1e..3ddf878 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -16,7 +16,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include #include @@ -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 new file mode 100644 index 0000000..a398107 --- /dev/null +++ b/template_for.h @@ -0,0 +1,120 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include + +#include + +#include + +namespace Alepha::Hydrogen ::detail:: template_for_each_m +{ + inline namespace exports + { + template< Tuple Type > + [[nodiscard]] + constexpr auto + template_for( Type &tuple ) noexcept; + + 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 + operator <= ( Function &&func ) noexcept( noexcept( template_for_impl( tuple, std::forward< Function >( func ) ) ) ) + { + return template_for_impl( tuple, std::forward< Function >( func ) ); + } + + constexpr operator decltype( std::ignore ) () const= delete; + }; + + template< typename Tuple > + [[nodiscard]] + constexpr auto + template_for_impl( Tuple &tuple ) noexcept + { + return syntax_adaptor< Tuple >{ tuple }; + } + + template< typename Tuple > + [[nodiscard]] + constexpr auto + template_for_impl( const Tuple &tuple ) noexcept + { + return syntax_adaptor< const Tuple >{ tuple }; + } + + + template< Tuple Type > + [[nodiscard]] + constexpr auto + exports::template_for( Type &tuple ) noexcept + { + return template_for_impl( tuple ); + } + + template< Tuple Type > + [[nodiscard]] + constexpr auto + exports::template_for( const Type &tuple ) noexcept + { + return template_for_impl( tuple ); + } +} + +namespace Alepha::Hydrogen::inline exports::inline template_for_each_m +{ + using namespace detail::template_for_each_m::exports; +} diff --git a/template_for.test/0.cc b/template_for.test/0.cc new file mode 100644 index 0000000..ecffee0 --- /dev/null +++ b/template_for.test/0.cc @@ -0,0 +1,42 @@ +static_assert( __cplusplus > 2023'00 ); + +#include "../template_for.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.test/CMakeLists.txt b/template_for.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/template_for.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) diff --git a/template_for_each.h b/template_for_each.h deleted file mode 100644 index 79822da..0000000 --- a/template_for_each.h +++ /dev/null @@ -1,88 +0,0 @@ -static_assert( __cplusplus > 2020'99 ); - -#pragma once - -#include - -#include - -#include "Concepts.h" - -namespace Alepha::Hydrogen ::detail:: template_for_each_m -{ - inline namespace exports - { - constexpr void tuple_for_each( const std::tuple<> &, const Functional auto ) noexcept {} - - template< typename ... Args, typename Function > - constexpr void - tuple_for_each( 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 ); - } - - // Apply type_identity to all tuple elements - template< typename > struct type_identify_tuple; - - template< typename T > - using type_identify_tuple_t= typename type_identify_tuple< T >::type; - - template<> struct type_identify_tuple< std::tuple<> > { using type= std::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< 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 }; - } - } -} - -namespace Alepha::Hydrogen::inline exports::inline template_for_each_m -{ - using namespace detail::template_for_each_m::exports; -}