Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
LazyInitialized.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_LazyInitialized_h_
5#define _Stroika_Foundation_Execution_LazyInitialized_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <concepts>
10#include <functional>
11#include <mutex>
12
13#include "Stroika/Foundation/Common/Common.h"
14
15/*
16 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
17 */
18
20
21 /**
22 * \brief value-object, where the value construction is delayed until first needed (can be handy to avoid c++ include/initializer deadly embrace)
23 *
24 * Also can be used to 'lazy initialize' facilities that might be costly to setup, but might never be used.
25 *
26 * Can be used to initialize a static constant object - declared at file scope - dependent on another file-scope data object,
27 * without incurring the pain of static initialization problems (before main). Often this is not needed, if you just
28 * make the dependent objects constexpr. But sometimes you cannot do that.
29 *
30 * LazyInitialized<T> acts mostly like a T (as much as I could figure out how to).
31 *
32 * This object (at least the magic init part) - is fully internally synchronized (though other operations of T itself are in general not).
33 *
34 * This object CAN be constructed before main, and accessed before main (after constructed) - but its up to caller to assure
35 * the 'oneTimeGetter' is safe to call when called.
36 *
37 * @aliases for ConstantProperty, 'virtual constant', VirtualConstant
38 *
39 * \note If you have an object 'obj' which MAYBE a T, or LazyInitialized<T>, you can convert it to type T
40 * with static_cast<T> (obj).
41 *
42 * \par Example Usage
43 * \code
44 * // say not legal to call EVP_md5 til you've initialized openssl, and maybe you never will - but still want to declare but
45 * // not use these constants - or at least declare the constants in a file/module (file scope so constructed before main) and
46 * // initialize openssl from after main starts?
47 * const LazyInitialized<DigestAlgorithm> DigestAlgorithms::kMD5{[] () { return ::EVP_md5 (); }};
48 * \endcode
49 *
50 * \par Example Usage
51 * \code
52 * const LazyInitialized<Sequence<filesystem::path>> Execution::kPath{[] () -> Sequence<filesystem::path> {
53 * if (const char* env_p = std::getenv ("PATH")) {
54 * String pathVar = String::FromNarrowSDKString (env_p);
55 * return pathVar.Tokenize ({':'}).Map<Sequence<filesystem::path>> ([] (auto i) { return i.template As<filesystem::path> (); });
56 * }
57 * return {};
58 * }};
59 * \endcode
60 *
61 * \par Example ALTERNATIVE (use lambda as with LazyInitialize but directly invoke) - but this invoked before main()
62 * \code
63 * const Sequence<filesystem::path> Execution::kPath{[] () -> Sequence<filesystem::path> {
64 * if (const char* env_p = std::getenv ("PATH")) {
65 * String pathVar = String::FromNarrowSDKString (env_p);
66 * return pathVar.Tokenize ({':'}).Map<Sequence<filesystem::path>> ([] (auto i) { return i.template As<filesystem::path> (); });
67 * }
68 * return {};
69 * } ()};
70 * \endcode
71 *
72 * \par Example Usage
73 * \code
74 * inline String kXGetter_ () { return "X"; }
75 * const LazyInitialized<String> kX {kXGetter_};
76 * ...
77 * const String a = kX;
78 * \endcode
79 *
80 * \note it would be HIGHLY DESIRABLE if C++ allowed operator'.' overloading, as accessing one of these
81 * values without assigning to a temporary first - means that you cannot directly call its methods.
82 * That's a bit awkward.
83 *
84 * So if you have a type T, with method m(), and variable of type T t.
85 * Your starter code might be:
86 * T t;
87 * t.m ();
88 * When you replace 'T t' with
89 * ConstantProperty<T> t;
90 * you must call t().m();
91 * OR
92 * you must call t->m();
93 *
94 * \note C++ also only allows one level of automatic operator conversions, so things like comparing
95 * optional<T> {} == LazyInitialized<T,...> {} won't work. To workaround, simply
96 * apply () after the LazyInitialized<> instance.
97 */
98 template <typename T>
100 public:
101 /**
102 * oneTimeGetter is a function (can be a lambda()) which computes the given value. It is called
103 * just once, and LAZILY, the first time the given VirtualConstant value is required.
104 *
105 * LazyInitialized (ONE TIME GETTER) - is the normal way to use LazyInitialized
106 * LazyInitialized (T) - somewhat pointless, but you can do it....
107 * copy-constructible
108 */
109 LazyInitialized () = delete;
110 template <invocable F>
111 constexpr LazyInitialized (F&& oneTimeGetter)
112 requires (convertible_to<invoke_result_t<F>, T>);
113 constexpr LazyInitialized (const T& v);
114 constexpr LazyInitialized (const LazyInitialized&) = delete;
115
116 public:
117 /**
118 */
119 nonvirtual LazyInitialized& operator= (const LazyInitialized&) = delete;
120 nonvirtual LazyInitialized& operator= (const T&);
121
122 public:
123 /**
124 */
125 constexpr ~LazyInitialized ();
126
127 public:
128 /**
129 * A LazyInitialized can be automatically assigned to its underlying base type.
130 * Due to how conversion operators work, this won't always be helpful (like with overloading
131 * or multiple levels of conversions). But when it works (80% of the time) - its helpful.
132 */
133 nonvirtual constexpr operator const T () const;
134
135 public:
136 /**
137 * Just use the function syntax, and you get back the initialized value.
138 *
139 * \par Example Usage
140 * \code
141 * namespace PredefinedInternetMediaType { const inline Execution::LazyInitialized<InternetMediaType> kPNG...
142 *
143 * bool checkIsImage1 = PredefinedInternetMediaType::kPNG().IsA (InternetMediaTypes::Wildcards::kImage);
144 * \endcode
145 */
146 nonvirtual const T operator() () const;
147
148 public:
149 /**
150 * Just use the operator-> syntax, and you get back the wrapper objects value (initializing if needed).
151 *
152 * \par Example Usage
153 * \code
154 * namespace PredefinedInternetMediaType { const inline Execution::LazyInitialized<InternetMediaType> kPNG = ...
155 *
156 * bool checkIsImage2 = PredefinedInternetMediaType::kPNG->IsA (InternetMediaTypes::Wildcards::kImage);
157 * \endcode
158 */
159 nonvirtual T* operator->();
160 nonvirtual const T* operator->() const;
161
162 private:
163 mutable once_flag fOnceFlag_; // cannot go in union cuz this 'discriminates' the union
164 // small space savings - don't need both getter and value at same time
165 union {
166 mutable T fValue_;
167 mutable function<T (void)> fOneTimeGetter_;
168 };
169
170 private:
171 nonvirtual T& Getter_ ();
172 nonvirtual const T& Getter_ () const;
173 };
174
175}
176
177/*
178 ********************************************************************************
179 ***************************** Implementation Details ***************************
180 ********************************************************************************
181 */
182#include "LazyInitialized.inl"
183
184#endif /*_Stroika_Foundation_Execution_LazyInitialized_h_*/
value-object, where the value construction is delayed until first needed (can be handy to avoid c++ i...