Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
MoreConfiguration.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
7#include "Stroika/Foundation/Common/Property.h"
10#include "Stroika/Foundation/Execution/WaitableEvent.h"
11
12#include "MoreConfiguration.h"
13
14using namespace std;
15
16using namespace Stroika::Foundation;
20using namespace Stroika::Foundation::Execution;
21
22using namespace StroikaSample;
23using namespace StroikaSample::AppSettings;
24
25namespace {
26 WaitableEvent sWaitableEvent_; // some thread could be waiting on this, and perform some reactive task when the module settings change
27
28}
29
30/*
31 ********************************************************************************
32 ********** AppSettings::Private_::MoreOptionsData_Storage_IMPL_ ****************
33 ********************************************************************************
34 */
35
36AppSettings::Private_::MoreOptionsData_Storage_IMPL_::MoreOptionsData_Storage_IMPL_ ()
37 : fOptionsFile_{/*
38 * Any module name will do. This will map (by default) to a MyModule.json file in XXX.
39 * If you require a single configuration file 'Main" might be a better module name.
40 * But if you have multiple modules with configuration data, pick a name that matches that module,
41 * and they will all be stored under a folder for all your apps configuration.
42 */
43 "MyModule"sv,
44
45 /*
46 * C++ doesn't have intrinsically enough metadata to effectively serialize deserialize data, but its close.
47 * You have to give it class mappings, and other non-builtin types mappings, so that it can serialize.
48 *
49 * Note - this serializing logic is VERY widely useful outside of configuration - for example it can be used
50 * to provide WebService/REST interfaces, or for debugging/logging output.
51 */
52 [] () -> ObjectVariantMapper {
54 mapper.AddClass<MoreOptionsData_> ({
55 {"Enabled"sv, &MoreOptionsData_::fEnabled},
56 {"Last-Synchronized-At"sv, &MoreOptionsData_::fLastSynchronizedAt},
57 });
58 return mapper;
59 }(),
60
61 /*
62 * Hooks for versioning, to manage as your application evolves and the configuration data changes
63 */
65
66 /*
67 * Hook to decide the folder (and filename pattern) where the configuration data will be stored.
68 *
69 * This defaults to
70 * FileSystem::WellKnownLocations::GetApplicationData () + appName + String{IO::FileSystem::kPathComponentSeperator} + moduleName + suffix
71 * or folder:
72 * "/var/opt/Put-Your-App-Name-Here" or "C:\ProgramData\Put-Your-App-Name-Here"
73 * and this module configuration file would be:
74 * "/var/opt/Put-Your-App-Name-Here/MyModule.json" OR
75 * "C:/ProgramData/Put-Your-App-Name-Here/MyModule.json" OR
76 *
77 * \note - this function does NOT create the 'Put-Your-App-Name-Here' folder first, and will NOT persist
78 * files if this folder does not exist.
79 *
80 * Callers can easily replace the default function provided in OptionsFile::mkFilenameMapper - just
81 * don't call that and provide your own lambda - to create the folder.
82 *
83 * But a better pattern is to create the folder in your application installer, typically.
84 */
85 OptionsFile::mkFilenameMapper ("Put-Your-App-Name-Here"sv)}
86 , fActualCurrentConfigData_{fOptionsFile_.Read<MoreOptionsData_> (MoreOptionsData_{})}
87{
88 Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
89}
90MoreOptionsData_ AppSettings::Private_::MoreOptionsData_Storage_IMPL_::Get () const
91{
92 // no locking required here for thread safety.
93 // This is always done inside of a read or a full lock by ModuleGetterSetter
94 return fActualCurrentConfigData_;
95}
96void AppSettings::Private_::MoreOptionsData_Storage_IMPL_::Set (const MoreOptionsData_& v)
97{
98 // no locking required here for thread safety.
99 // This is always done inside of a write lock by ModuleGetterSetter
100 fActualCurrentConfigData_ = v;
101 fOptionsFile_.Write (v);
102}
103
104/*
105 ********************************************************************************
106 *************************** AppSettings::TestUse1 ******************************
107 ********************************************************************************
108 */
109void AppSettings::TestUse1 ()
110{
111 // This will be by far the most common use pattern - just read some field of the configuration object
112 if (gModuleConfiguration.Get ().fEnabled) {
113 // do something
114 }
115}
116
117/*
118 ********************************************************************************
119 *************************** AppSettings::TestUse2 ******************************
120 ********************************************************************************
121 */
122void AppSettings::TestUse2 ()
123{
124 // or read several fields all guaranteed within this same snapshot (not holding a lock duing the action)
125 auto d = gModuleConfiguration.Get ();
126 // lock not held here so configuration could change but this code remains safe and crash free
127 if (d.fEnabled and d.fLastSynchronizedAt) {
128 // do something
129 }
130}
131
132/*
133 ********************************************************************************
134 *************************** AppSettings::TestUse3 ******************************
135 ********************************************************************************
136 */
137void AppSettings::TestUse3 ()
138{
139 if (gModuleConfiguration.Get ().fEnabled) {
140 // a non-atomic update of the entire MoreOptionsData_ object
141 auto n = gModuleConfiguration.Get ();
142 n.fEnabled = false; // change something in 'n' here
143 gModuleConfiguration.Set (n);
144 }
145}
146
147/*
148 ********************************************************************************
149 *************************** AppSettings::TestUse4 ******************************
150 ********************************************************************************
151 */
152void AppSettings::TestUse4 ()
153{
154 // Use Update () to atomically update data
155 // Use the return value to tell if a real change was made (so you can invoke some sort of notification/action)
156 static const Duration kMinTime_ = 2min;
157 if (gModuleConfiguration.Update ([] (const MoreOptionsData_& data) -> optional<MoreOptionsData_> {
158 if (data.fLastSynchronizedAt and *data.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) {
159 MoreOptionsData_ result = data;
160 result.fLastSynchronizedAt = DateTime::Now ();
161 return result;
162 }
163 return {};
164 })) {
165 sWaitableEvent_.Set (); // e.g. trigger someone to wakeup and used changes? - no global lock held here...
166 }
167}
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
static const ModuleDataUpgraderType kDefaultUpgrader
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
STL namespace.