forked from Alepha/Alepha
Create C++26(?) emulation of template for
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 ) { ...; }; ```
This commit is contained in:
@ -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 )
|
||||
|
@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 );
|
||||
#include <istream>
|
||||
|
||||
#include <Alepha/Capabilities.h>
|
||||
#include <Alepha/template_for_each.h>
|
||||
#include <Alepha/template_for.h>
|
||||
#include <Alepha/string_algorithms.h>
|
||||
#include <Alepha/Concepts.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;
|
||||
|
@ -7,7 +7,7 @@ static_assert( __cplusplus > 2020'99 );
|
||||
#include <ostream>
|
||||
|
||||
#include <Alepha/Capabilities.h>
|
||||
#include <Alepha/template_for_each.h>
|
||||
#include <Alepha/template_for.h>
|
||||
#include <Alepha/string_algorithms.h>
|
||||
#include <Alepha/delimited_list.h>
|
||||
#include <Alepha/Concepts.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;
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ static_assert( __cplusplus > 2020'99 );
|
||||
#include <iomanip>
|
||||
|
||||
#include <Alepha/function_traits.h>
|
||||
#include <Alepha/template_for_each.h>
|
||||
#include <Alepha/template_for.h>
|
||||
|
||||
#include <Alepha/IOStreams/String.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;
|
||||
|
@ -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;
|
||||
|
120
template_for.h
Normal file
120
template_for.h
Normal file
@ -0,0 +1,120 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <Alepha/Concepts.h>
|
||||
|
||||
#include <Alepha/Reflection/tuplizeAggregate.h>
|
||||
|
||||
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;
|
||||
}
|
42
template_for.test/0.cc
Normal file
42
template_for.test/0.cc
Normal file
@ -0,0 +1,42 @@
|
||||
static_assert( __cplusplus > 2023'00 );
|
||||
|
||||
#include "../template_for.h"
|
||||
|
||||
#include <Alepha/Testing/test.h>
|
||||
#include <Alepha/Testing/TableTest.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <Alepha/Utility/enroll.h>
|
||||
|
||||
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" },
|
||||
};
|
||||
};
|
1
template_for.test/CMakeLists.txt
Normal file
1
template_for.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
unit_test( 0 )
|
@ -1,88 +0,0 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#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;
|
||||
}
|
Reference in New Issue
Block a user