1
0
forked from Alepha/Alepha

Unify the evaluation helpers implementations.

This commit is contained in:
2023-10-20 03:54:22 -04:00
parent f5840e15ab
commit 0b63658815
18 changed files with 204 additions and 238 deletions

View File

@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 );
#include <Alepha/Testing/test.h>
#include <Alepha/Testing/TableTest.h>
#include <Alepha/Utility/evaluation.h>
#include <Alepha/Utility/evaluation_helpers.h>
#include "../word_wrap.h"

View File

@ -1,57 +0,0 @@
static_assert( __cplusplus > 2020'00 );
#pragma once
#include <Alepha/Alepha.h>
#include <tuple>
#include <utility>
namespace Alepha::Hydrogen::Utility
{
inline namespace exports { inline namespace evaluation {} }
namespace detail::evaluation
{
struct evaluate_t {};
struct enroll_t {};
struct lambaste_t {};
inline namespace exports
{
inline constexpr evaluate_t evaluate;
inline constexpr enroll_t enroll;
inline constexpr lambaste_t lambaste;
}
template< typename Function >
decltype( auto )
operator <=( evaluate_t, Function &&init )
{
return std::forward< Function >( init )();
}
template< typename Init >
auto
operator <=( enroll_t, Init init )
{
struct {} registration;
(void) ( evaluate <=init );
return registration;
}
template< typename Value >
auto
operator <=( lambaste_t, Value value )
{
return [value]{ return value; };
}
}
namespace exports::evaluation
{
using namespace detail::evaluation::exports;
}
}

View File

@ -0,0 +1,171 @@
static_assert( __cplusplus > 2020'00 );
#pragma once
#include <Alepha/Alepha.h>
#include <tuple>
#include <utility>
namespace Alepha::Hydrogen::Utility ::detail:: evaluation_helpers
{
inline namespace exports
{
/*!
* Mechanism to clarify immediately-invoked lambdas.
*
* Immediately invoked lambdas are a very useful tool.
*
* ```
* const auto mySortedArray= [&]
* {
* std::vector< std::string > rv;
* // Populate it...
* std::sort( begin( rv ), end( rv ) );
* return rv;
* }();
* ```
*
* The above code defines a sorted vector as const by immediately invoking a lambda to do it. The problem,
* however, is that it is entirely unclear whether a lambda is being invoked or defined until the very end
* of the lambda's definition is reached. This can become very confusing when there are several nested
* levels of lambdas and they get a bit large.
*
* `evaluate` creates a hook that makes it more clear that it is an immediately-invoked lambda:
*
* ```
* const auto mySortedArray= evaluate <=[&]
* {
* std::vector< std::string > rv;
* // Populate it...
* std::sort( begin( rv ), end( rv ) );
* return rv;
* };
* ```
*
* Because `evaluate <=` precedes the lambda definition, it is quite clear that something else is going on
* here. The `<=` in this case should be thought of as a `fat left arrow` -- the lambda is being put
* through an evaluation operation.
*/
inline struct evaluate_t {} evaluate;
/*!
* Mechanism to define initializer blocks.
*
* C++ does not have initializer blocks, but it does have the ability to define variables that are set to
* values before `main` is invoked. It is possible to use constructors or functions to leverage this fact
* and make blocks of code that run before main:
*
* ```
* struct MyThing
* {
* MyThing()
* {
* std::cout << "Hello World, before main!" << std::endl;
* }
* } beforeMain;
* ```
*
* The above code works such that, as a side-effect of the construction of `beforeMain`, the desired code is
* run. However, it is rather cumbersome. There are ways of simplifying this:
*
* ```
* int beforeMainFunction() { std::cout << "Hello World, before main!" << std::endl; return 42; }
* const int beforeMain= beforeMainFunction();
* ```
*
* While this is a bit less confusing, there's still a need to define a function and return a dummy
* variable. Lambdas with immediate invocation syntax (see `evaluate`) can make this nicer still:
*
* ```
* const int beforeMain= evaluate <=[] { std::cout << "Hello World, before main!" << std::endl; return 0; };
* ```
*
* That is a bit better, but that pesky dummy value is still there. The fact that this is an int is going
* to be a potential source of confusion. Instead, we want to keep the reader focused on the fact that code
* is being run, and disguise the fact that there's a variable involved.
*
* `enroll` creates a hook by which the above techniques can be made more clear that it one is running
* pre-main code.
*
* ```
* auto myInitBlock= enroll <=[]
* {
* std::cout << "Hello World, before main!" << std::endl;
* };
* ```
*
* Because `enroll <=` precedes the lambda definition it is quite clear that something else is going on
* here. The `<=` in this case should be thought of as a `fat left arrow` -- the lambda is being given
* to `enroll` to be used as an initializer block.
*/
inline struct enroll_t {} enroll;
/*!
* Mechanism to define lambda capture of a value.
*
* Sometimes it's useful or necessary to take a known value and wrap it in a function-like interface. Some
* APIs let programmers provide functions which act as customization points. Sometimes the value is already
* at hand, and it needs to be wrapped in a lambda.
*
* For example:
*
* ```
* auto wrapped= [myValue] { return myValue; };
* ```
*
* While the above is perfectly adequate, it is a bit cumbersome. `myValue` is used twice, there's a lot of
* mechanical syntax for lambda function definition, etc. `lambaste` provides a simpler alternative:
*
* ```
* auto wrapped= lambaste <=myValue;
* ```
*
* Because `lambaste <=` precedes the variable, it is clear that something is going on here. The `<=` in
* this case should be thought of as a `fat left arrow` -- the value is being given to `lambaste` to be used
* in constructing a function-object.
*
* @note Lambaste is a stupid pun -- a better name might be in order.
*/
inline struct lambaste_t {} lambaste;
}
template< typename Function >
constexpr decltype( auto )
operator <=( evaluate_t, Function &&func ) noexcept( noexcept( std::forward< Function >( func )() ) )
{
return std::forward< Function >( func )();
}
template< typename Function >
struct registration
{
explicit registration( Function f ) { evaluate <=f; }
};
template< typename Function >
constexpr auto
operator <=( enroll_t, Function &&func ) noexcept
{
return registration{ std::forward< Function >( func ) };
}
template< typename ValueType >
constexpr auto
as_func( ValueType value ) noexcept( std::is_nothrow_move_constructible_v< ValueType > )
{
return [value= std::move( value )]() -> std::decay_t< ValueType > { return value; };
}
template< typename ValueType >
constexpr decltype( auto )
operator <=( lambaste_t, ValueType value ) noexcept( noexcept( as_func( std::move( value ) ) ) )
{
return as_func( std::move( value ) );
}
}
namespace Alepha::Hydrogen::Utility::inline exports::inline evaluation_helpers
{
using namespace detail::evaluation_helpers::exports;
}