Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Activity.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_Activity_h_
5#define _Stroika_Foundation_Execution_Activity_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
11
12/*
13 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
14 */
15
17
18 namespace Private_ {
19 namespace Activities_ {
20 /**
21 * \note intentionally omit virtual DTOR because then the subclasses cannot be constexpr (at least it appears so in c++17).
22 * And they are never destroyed by ptr to a base class type, so this isn't needed.
23 */
24 struct AsStringObj_ {
25 constexpr AsStringObj_ () noexcept = default;
26 virtual Characters::String AsString () const = 0;
27 };
28 struct StackElt_ {
29 const AsStringObj_* fActivity{};
30 const StackElt_* fPrev{};
31 };
32 thread_local inline const StackElt_* sTop_{nullptr};
33 }
34 }
35
36 /**
37 * An Activity is typically a static const or sometimes even constexpr object which contains a description of
38 * an ongoing activity. They are generally not meant to be stored or copied, but REFERENCED with DeclareActivity
39 * to form a 'current activity stack'.
40 *
41 * \par Example Usage
42 * \code
43 * static constexpr Activity kBuildingThingy_{ "building thingy"sv };
44 * static const auto kOtherActivity = Activity<String>{ "abc" };
45 * // automatic variable activity OK as long as its lifetime longer than reference in DeclareActivity
46 * auto otherActivity = Activity<String> { "abc" + argument }; // activities can be stack based, but these cost more to define
47 * auto lazyEvalActivity = LazyEvalActivity ([&] () -> String { return argument.Repeat (5) + "x"; });
48 * // then for how to use activity object - see DeclareActivity
49 * \endcode
50 *
51 * \note the intended purpose of this is to provide better 'context' for default exception messages, when exceptions are
52 * thrown (by Execution::Throw ()). @see CaptureCurrentActivities
53 */
54 template <convertible_to<Characters::String> STRINGISH_T = Characters::String>
56 public:
57 /**
58 * @todo unclear why we need to have two overloads here - but hard todo CTOR so works for arrays otherwise (see "before Stroika 3.0d21 had trouble compiling this")
59 */
60 constexpr Activity (const STRINGISH_T& arg)
61 requires (is_array_v<STRINGISH_T>);
62 constexpr Activity (const STRINGISH_T& arg)
63 requires (not is_array_v<STRINGISH_T>);
64
65 Activity (const Activity& src) = default;
66
67 public:
68 virtual Characters::String AsString () const override;
69
70 private:
71 STRINGISH_T fArg_;
72 };
73
74 /**
75 */
76 template <convertible_to<Characters::String> CTOR_ARG>
77 Activity (const CTOR_ARG& b) -> Activity<CTOR_ARG>;
78
79 /**
80 * When creating the activity would be expensive, just capture it in a lambda, and only convert that lambda to
81 * an actual string if/when CaptureCurrentActivities is called.
82 *
83 * \note would LIKE to know how to do this as a template specialization of Activity, but haven't figured that out yet.
84 *
85 * \par Example Usage
86 * \code
87 * auto scanningThisAddress = LazyEvalActivity ([&] () -> String { return "scanning ports on {}"_f (ia); });
88 * DeclareActivity da{&scanningThisAddress};
89 * \endcode
90 */
91 template <typename CTOR_ARG>
93 public:
94 constexpr LazyEvalActivity (const CTOR_ARG& arg)
95 requires (is_invocable_r_v<Characters::String, CTOR_ARG>);
96
97 public:
98 virtual Characters::String AsString () const override;
99
100 private:
102 };
103
104 /**
105 * \brief Checks if CaptureCurrentActivities() would produce a non-empty stack (but faster)
106 */
107 bool AnyCurrentActivities ();
108
109 /**
110 * \brief Returns a copyable preservable version of the current activities stack.
111 *
112 * 'render' each current activity on the current thread's activity stack as an Activity<> (so String based), and return the full
113 * list as a copyable stack of activities.
114 *
115 * \note the intended purpose of this is to provide better 'context' for default exception messages, when exceptions are
116 * thrown (by Execution::Throw ()).
117 *
118 * \note the TOP of the stack is the most specific activity, and the bottom of the stack is the initial, outermost task.
119 */
121
122 /**
123 * Push the argument Activity onto the current thread's Activity stack in the constructor, and pop it off in the destructor.
124 *
125 * An exception captures the activities in force when it is THROWN with Execution::Throw (), not those in force when
126 * it was constructed. So an exception built in one context (a static const, say) and thrown in another reports the
127 * context of the throw. Throw () fills in only an exception that specified no activities of its own (any Stroika
128 * Exception<>), and a bare `throw X{}` gets none. For a third-party exception, wrap it in a NestedException and
129 * Throw () that.
130 *
131 * Each activity's AsString () runs at that Throw (), while its DeclareActivity is still in scope - so a
132 * LazyEvalActivity's lambda may capture by reference.
133 *
134 * \par Example Usage
135 * \code
136 * static constexpr Activity kBuildingThingy_ {"building thingy"sv };
137 * try {
138 * DeclareActivity declareActivity { &kBuildingThingy_ };
139 * doBuildThing (); // Execution::Throw () any Exception<>
140 * }
141 * catch (...) {
142 * String exceptionMsg = Characters::ToString (current_exception ());
143 * Assert (exceptionMsg.Contains (kBuildingThingy_.AsString ())); // exception e while building thingy...
144 * }
145 * \endcode
146 *
147 * \note DeclareActivity objects must be destroyed in the reverse order of their construction, on the thread that
148 * constructed them (Asserted in the destructor) - so in practice automatic variables, and never one that lives
149 * across a co_await, which breaks that order even on one thread. The stack is per-thread: work handed to
150 * another thread (a thread pool task, say) does not see it.
151 */
152 template <typename ACTIVITY>
154 public:
155 DeclareActivity () = delete;
156 DeclareActivity (const DeclareActivity&) = delete;
157 /**
158 * The caller must always declare an activity object (an Activity<> or LazyEvalActivity<>) whose address can be
159 * taken, and whose lifetime exceeds that of the DeclareActivity object.
160 *
161 * \note the activity argument MAY be nullptr, in which case this does nothing.
162 *
163 * This is allowed to facilitate examples like:
164 * \code
165 * DeclareActivity declareActivity { flag ? &kSomeActivity_ : nullptr }; // so we only conditionally declare the activity
166 * \endcode
167 *
168 * I considered using optional<ACTIVITY> - but tricky since we pass in pointer - and really not much point. This has practically zero
169 * overhead, and is easy enough to understand and use.
170 */
171 DeclareActivity (const ACTIVITY* activity) noexcept;
173
174 public:
175 nonvirtual DeclareActivity& operator= (const DeclareActivity&) = delete;
176
177 private:
178 Private_::Activities_::StackElt_ fNewTopOfStackElt_;
179 };
180
181}
182
183/*
184 ********************************************************************************
185 ***************************** Implementation Details ***************************
186 ********************************************************************************
187 */
188#include "Activity.inl"
189
190#endif /*_Stroika_Foundation_Execution_Activity_h_*/
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Containers::Stack< Activity<> > CaptureCurrentActivities()
Returns a copyable preservable version of the current activities stack.
Definition Activity.cpp:19
bool AnyCurrentActivities()
Checks if CaptureCurrentActivities() would produce a non-empty stack (but faster)
Definition Activity.cpp:39