forked from Alepha/Alepha
85 lines
2.6 KiB
C++
85 lines
2.6 KiB
C++
static_assert( __cplusplus > 2020'99 );
|
|
|
|
#pragma once
|
|
|
|
#include <Alepha/Alepha.h>
|
|
|
|
#include <tuple>
|
|
#include <utility>
|
|
|
|
namespace Alepha::Hydrogen::Utility ::detail:: enroll_m
|
|
{
|
|
inline namespace exports
|
|
{
|
|
/*!
|
|
* 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;
|
|
}
|
|
|
|
template< typename Function >
|
|
struct registration
|
|
{
|
|
explicit registration( Function f ) { f(); }
|
|
};
|
|
|
|
template< typename Function >
|
|
constexpr auto
|
|
operator <=( enroll_t, Function &&func ) noexcept
|
|
{
|
|
return registration{ std::forward< Function >( func ) };
|
|
}
|
|
}
|
|
|
|
namespace Alepha::Hydrogen::Utility::inline exports::inline enroll_m
|
|
{
|
|
using namespace detail::enroll_m::exports;
|
|
}
|