diff --git a/Console.cc b/Console.cc index ded34f2..4884630 100644 --- a/Console.cc +++ b/Console.cc @@ -15,11 +15,12 @@ static_assert( __cplusplus > 2020'99 ); #include +#include + #include #include "Enum.h" #include "ProgramOptions.h" -#include "StaticValue.h" #include "string_algorithms.h" #include "AutoRAII.h" @@ -46,6 +47,7 @@ namespace Alepha::Hydrogen ::detail:: Console_m using namespace std::literals::string_literals; using namespace Utility::exports::evaluation_helpers; + using Utility::StaticValue; namespace C { diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h index a2f8f49..8513535 100644 --- a/IOStreams/delimiters.h +++ b/IOStreams/delimiters.h @@ -8,8 +8,6 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include - #include namespace Alepha::Hydrogen::IOStreams ::detail:: delimiters_m diff --git a/ProgramOptions.cc b/ProgramOptions.cc index 461d14b..0250b6c 100644 --- a/ProgramOptions.cc +++ b/ProgramOptions.cc @@ -7,9 +7,10 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include #include +#include + #include namespace Alepha::Hydrogen ::detail:: ProgramOptions_m @@ -24,6 +25,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m } using namespace std::literals::string_literals; + using Utility::StaticValue; struct OptionMissingArgumentError : virtual std::runtime_error diff --git a/StaticValue.h b/StaticValue.h deleted file mode 100644 index f371f78..0000000 --- a/StaticValue.h +++ /dev/null @@ -1,157 +0,0 @@ -static_assert( __cplusplus > 2020'99 ); - -#pragma once - -#include - -#include - -#include - -namespace Alepha::Hydrogen ::detail:: StaticValue_m -{ - inline namespace exports - { - template< typename T, typename init_helper > - class StaticValue; - } - - template< typename T > - struct default_init - { - constexpr decltype( auto ) - operator() () const noexcept - { - return new T{}; - } - }; - - /*! - * Static value with low access overhead. - * - * In C++, function-static values in functions can have significant overhead to access, because they use an - * atomic check to guarantee threadsafe initialization. Alternatively, namespace-scope static values have their - * own initialization problems, because of the initialization order rules in C++. `Alepha::StaticValue` is a - * mechanism to provide a halfway-between solution. `Alepha::StaticValue` objects should be defined at - * namespace-scope. Unlike normal namespace-scope variables, they are initialized at first access. Unlike - * function-static values, the initialization is not guarded by an atomic access. - * - * `StaticValue` objects solve the initialization order issues of normal namespace-statics because it will - * initialize on first use. They solve the performance issues of function-statics, because of the - * non-atomic initialization technique. This means that the most appropriate use pattern is to guarantee - * initialization during a single-threaded portion of program execution. The idiom is meant to be - * a drop-in replacement (in usage syntax) for a function which returns a static value. However, it now has - * its own set of limitations -- it is not possible to initialize in a threadsafe manner. - * - * Example usage: - * - * ``` - * namespace MyComponent - * { - * StaticValue< std::string > globalMessage; - * - * auto initGlobalMessage= globalMessage.init(); - * - * void - * printGlobalMessage() - * { - * std::cout << globalMessage() << std::endl; - * } - * } - * ``` - * - * If `printGlobalMessage` is called from any static initializer, before `initGlobalMessage` is initialized, - * then that first use will initialize the value. If `initGlobalMessage` is initialized before any usage - * of `globalMessage`, then the value will be initialized by that initializer. - * - * For any program which does not start any threads before `main()` starts, this pattern (declare a - * `StaticValue` and declare an initializer step) will always be threadsafe and proper. For a program - * which can guarantee that a `StaticValue` is used in static initializers before `main()` or threads start, - * it is possible to decline making a static initializer call to `StaticValue::init`. - * - * Exploring this further, here's a typical function-static used with initializers: - * ``` - * namespace MyComponent - * { - * auto & - * registry() - * { - * static std::vector< std::string > registrations; - * return registrations; - * } - * - * auto registration= Alepha::enroll<=[] - * { - * registry().push_back( "Hello World" ); - * }; - * } - * ``` - * The above could be rewritten to have the effects of `StaticValue` thus: - * ``` - * namespace MyComponent - * { - * namespace storage - * { - * std::vector< std::string > *registrations= nullptr; - * } - * - * auto & - * registry() - * { - * using namespace storage; - * if( registrations == nullptr ) registrations= new decltype( *registrations ); - * return *registrations; - * } - * - * auto registration= Alepha::enroll<=[] - * { - * registry().push_back( "Hello World" ); - * }; - * } - * ``` - * However, that is complicated and difficult to get right. Thus `StaticValue` helps - * us by radically simplifying the declaration syntax: - * ``` - * namespace MyComponent - * { - * StaticValue< std::vector< std::string > > registrations; - * - * auto registration= Alepha::enroll<=[] - * { - * registrations().push_back( "Hello World" ); - * }; - * } - * ``` - */ - template< typename T, typename init_helper= default_init< T > > - class exports::StaticValue : boost::noncopyable - { - private: - T *storage= nullptr; - - public: - ~StaticValue() - { - delete storage; - } - - constexpr decltype( auto ) - get() noexcept - { - if( storage == nullptr ) [[unlikely]] storage= init_helper{}(); - assert( storage != nullptr ); - return *storage; - } - - constexpr decltype( auto ) - operator() () noexcept - { - return get(); - } - }; -} - -namespace Alepha::Hydrogen::inline exports::inline StaticValue_m -{ - using namespace detail::StaticValue_m::exports; -} diff --git a/Testing/test.h b/Testing/test.h index 48dbf8a..a9b7f9e 100644 --- a/Testing/test.h +++ b/Testing/test.h @@ -40,7 +40,7 @@ namespace Alepha::Hydrogen::Testing using namespace std::literals::string_literals; using namespace Utility::exports::evaluation_helpers; - using namespace Utility::exports::static_value; + using namespace Utility::exports::StaticValue_m; struct TestName { diff --git a/Utility/StaticValue.h b/Utility/StaticValue.h index 1af28c2..2ba6724 100644 --- a/Utility/StaticValue.h +++ b/Utility/StaticValue.h @@ -2,46 +2,158 @@ static_assert( __cplusplus > 2020'99 ); #pragma once -namespace Alepha::Hydrogen::Utility +#include + +#include + +#include + +#include + +namespace Alepha::Hydrogen::Utility ::detail:: StaticValue_m { - inline namespace exports { inline namespace static_value {} } - - namespace detail::static_value + inline namespace exports { - inline namespace exports {} + template< typename T, typename init_helper > + class StaticValue; + } - template< typename T > - struct default_init + template< typename T > + struct default_init + { + constexpr decltype( auto ) + operator() () const noexcept { - T *operator()() const { return new T{}; } - }; - - namespace exports - { - template< typename T, typename Init= default_init< T > > - struct StaticValue; + return new T{}; } + }; - template< typename T, typename Init > - struct exports::StaticValue - { - private: - T *storage= nullptr; - - public: - decltype( auto ) - get() - { - if( not storage ) storage= Init{}(); - return *storage; - } - - decltype( auto ) operator ()() { return get(); } - }; - } - - namespace exports::static_value + /*! + * Static value with low access overhead. + * + * In C++, function-static values in functions can have significant overhead to access, because they use an + * atomic check to guarantee threadsafe initialization. Alternatively, namespace-scope static values have their + * own initialization problems, because of the initialization order rules in C++. `Alepha::StaticValue` is a + * mechanism to provide a halfway-between solution. `Alepha::StaticValue` objects should be defined at + * namespace-scope. Unlike normal namespace-scope variables, they are initialized at first access. Unlike + * function-static values, the initialization is not guarded by an atomic access. + * + * `StaticValue` objects solve the initialization order issues of normal namespace-statics because it will + * initialize on first use. They solve the performance issues of function-statics, because of the + * non-atomic initialization technique. This means that the most appropriate use pattern is to guarantee + * initialization during a single-threaded portion of program execution. The idiom is meant to be + * a drop-in replacement (in usage syntax) for a function which returns a static value. However, it now has + * its own set of limitations -- it is not possible to initialize in a threadsafe manner. + * + * Example usage: + * + * ``` + * namespace MyComponent + * { + * StaticValue< std::string > globalMessage; + * + * auto initGlobalMessage= globalMessage.init(); + * + * void + * printGlobalMessage() + * { + * std::cout << globalMessage() << std::endl; + * } + * } + * ``` + * + * If `printGlobalMessage` is called from any static initializer, before `initGlobalMessage` is initialized, + * then that first use will initialize the value. If `initGlobalMessage` is initialized before any usage + * of `globalMessage`, then the value will be initialized by that initializer. + * + * For any program which does not start any threads before `main()` starts, this pattern (declare a + * `StaticValue` and declare an initializer step) will always be threadsafe and proper. For a program + * which can guarantee that a `StaticValue` is used in static initializers before `main()` or threads start, + * it is possible to decline making a static initializer call to `StaticValue::init`. + * + * Exploring this further, here's a typical function-static used with initializers: + * ``` + * namespace MyComponent + * { + * auto & + * registry() + * { + * static std::vector< std::string > registrations; + * return registrations; + * } + * + * auto registration= Alepha::enroll<=[] + * { + * registry().push_back( "Hello World" ); + * }; + * } + * ``` + * The above could be rewritten to have the effects of `StaticValue` thus: + * ``` + * namespace MyComponent + * { + * namespace storage + * { + * std::vector< std::string > *registrations= nullptr; + * } + * + * auto & + * registry() + * { + * using namespace storage; + * if( registrations == nullptr ) registrations= new decltype( *registrations ); + * return *registrations; + * } + * + * auto registration= Alepha::enroll<=[] + * { + * registry().push_back( "Hello World" ); + * }; + * } + * ``` + * However, that is complicated and difficult to get right. Thus `StaticValue` helps + * us by radically simplifying the declaration syntax: + * ``` + * namespace MyComponent + * { + * StaticValue< std::vector< std::string > > registrations; + * + * auto registration= Alepha::enroll<=[] + * { + * registrations().push_back( "Hello World" ); + * }; + * } + * ``` + */ + template< typename T, typename init_helper= default_init< T > > + class exports::StaticValue : boost::noncopyable { - using namespace detail::static_value::exports; - } + private: + T *storage= nullptr; + + public: + ~StaticValue() + { + delete storage; + } + + constexpr decltype( auto ) + get() noexcept + { + if( storage == nullptr ) [[unlikely]] storage= init_helper{}(); + assert( storage != nullptr ); + return *storage; + } + + constexpr decltype( auto ) + operator() () noexcept + { + return get(); + } + }; +} + +namespace Alepha::Hydrogen::Utility::inline exports::inline StaticValue_m +{ + using namespace detail::StaticValue_m::exports; }