ReXGlue SDK
Loading...
Searching...
No Matches
setting.h
Go to the documentation of this file.
1
11
12#pragma once
13
14#include <atomic>
15#include <filesystem>
16#include <memory>
17#include <mutex>
18#include <optional>
19#include <span>
20#include <string>
21#include <string_view>
22#include <type_traits>
23#include <utility>
24#include <vector>
25
26#include <rex/module.h>
27#include <rex/platform.h>
28#include <rex/result.h>
29#include <rex/types.h>
30#include <rex/utility.h>
31
32namespace rex {
33
34class ConsoleCommand;
35class SettingOwner;
37
38enum class SettingType : u8 {
39 kBool,
40 kI32,
41 kI64,
42 kU32,
43 kU64,
44 kF64,
45 kString,
47};
48
50enum class SettingLayer : u8 {
51 kDefault,
52 kTitle,
53 kFile,
56 kRuntime,
58};
59
60enum class SettingLife : u8 {
61 kLive,
62 kRestart,
63 kStartupOnly,
64 kUntilLaunch,
65};
66
68 std::optional<f64> min;
69 std::optional<f64> max;
70 std::vector<std::string> allowed;
72 bool no_save = false;
73 bool debug_only = false;
74 bool key_chord = false;
75};
76
78struct Range {
79 f64 min;
80 f64 max;
81};
82
84struct Allowed {
85 std::vector<std::string> values;
86
87 template <typename... Texts>
88 explicit Allowed(Texts... texts) : values{std::string(texts)...} {}
89};
90
91inline constexpr SettingLife kRestart = SettingLife::kRestart;
92inline constexpr SettingLife kStartupOnly = SettingLife::kStartupOnly;
93inline constexpr SettingLife kUntilLaunch = SettingLife::kUntilLaunch;
94inline constexpr struct NoSave {
95} kNoSave;
96inline constexpr struct DebugOnly {
97} kDebugOnly;
98inline constexpr struct KeyChordTrait {
99} kKeyChord;
100
102class REX_API SettingBase : public NonCopyable {
103 public:
104 virtual ~SettingBase();
105
107 const std::string& GetName() const;
108
109 const std::string& GetHelp() const;
110
111 SettingType GetType() const;
112
117
118 SettingOwner& GetOwner() const;
119
124
126 bool IsFrozen() const;
127
129 bool IsRestartPending() const;
130
132 std::string GetText() const;
133
134 std::string GetDefaultText() const;
135
141 [[nodiscard]] Result<void> Check(std::string_view text) const;
142
149 [[nodiscard]] Result<void> SetText(SettingLayer layer, std::string_view text);
150
154 [[nodiscard]] Result<void> Reset();
155
156 protected:
157 SettingBase(SettingOwner& owner, std::string_view name, SettingType type, std::string_view help);
158
163
169 [[nodiscard]] Result<void> SetFromCode(std::string_view text);
170
171 private:
172 friend class SettingOwner;
173 friend class SettingsRegistry;
174
175 virtual bool Store(std::string_view text) = 0;
176 virtual void Publish() = 0;
177 virtual std::string Render() const = 0;
178 virtual std::string RenderDefault() const = 0;
179 virtual void Notify() = 0;
180
181 struct State;
182 std::unique_ptr<State> state_;
183};
184
190template <typename T>
191class Setting final : public SettingBase {
192 public:
198 template <typename... Traits>
199 Setting(SettingOwner* owner, std::string_view name, const T& default_value, std::string_view help,
200 Traits... traits);
201
203 T Get() const;
204
210 [[nodiscard]] Result<void> Set(const T& value);
211
217 [[nodiscard]] Result<void> Lock(const T& value);
218
225 [[nodiscard]] ScopeGuard OnChange(MoveOnlyFunction<void(const T&)> fn);
226
227 private:
228 static constexpr bool kScalar =
229 !std::is_same_v<T, std::string> && !std::is_same_v<T, std::filesystem::path>;
230 using Storage = std::conditional_t<kScalar, std::atomic<T>, T>;
231
232 bool Store(std::string_view text) override;
233 void Publish() override;
234 std::string Render() const override;
235 std::string RenderDefault() const override;
236 void Notify() override;
237
238 T GetActive() const;
239
240 T default_;
241 T staged_;
242 Storage active_;
243 mutable std::mutex mutex_;
244 u64 next_watch_ = 1;
245 std::vector<std::pair<u64, std::shared_ptr<MoveOnlyFunction<void(const T&)>>>> watches_;
246};
247
249class REX_API SettingOwner : public NonCopyable {
250 public:
254 explicit SettingOwner(std::string_view scope);
255
257 virtual ~SettingOwner();
258
259 const std::string& GetScope() const;
260
264 std::span<SettingBase* const> GetOwnedSettings() const;
265
266 private:
267 friend class SettingBase;
268 friend class ConsoleCommand;
269 friend class SettingsRegistry;
270 struct State;
271 std::unique_ptr<State> state_;
272};
273
274namespace detail {
275
276REX_API bool ParseSetting(std::string_view text, bool& out);
277REX_API bool ParseSetting(std::string_view text, i32& out);
278REX_API bool ParseSetting(std::string_view text, i64& out);
279REX_API bool ParseSetting(std::string_view text, u32& out);
280REX_API bool ParseSetting(std::string_view text, u64& out);
281REX_API bool ParseSetting(std::string_view text, f64& out);
282REX_API bool ParseSetting(std::string_view text, std::string& out);
283REX_API bool ParseSetting(std::string_view text, std::filesystem::path& out);
284
285REX_API std::string RenderSetting(bool value);
286REX_API std::string RenderSetting(i32 value);
287REX_API std::string RenderSetting(i64 value);
288REX_API std::string RenderSetting(u32 value);
289REX_API std::string RenderSetting(u64 value);
290REX_API std::string RenderSetting(f64 value);
291REX_API std::string RenderSetting(const std::string& value);
292REX_API std::string RenderSetting(const std::filesystem::path& value);
293
294REX_API std::filesystem::path ResolveSettingPath(const std::filesystem::path& value);
295
296template <typename T>
297constexpr SettingType TypeOf() {
298 if constexpr (std::is_same_v<T, bool>)
299 return SettingType::kBool;
300 else if constexpr (std::is_same_v<T, i32>)
301 return SettingType::kI32;
302 else if constexpr (std::is_same_v<T, i64>)
303 return SettingType::kI64;
304 else if constexpr (std::is_same_v<T, u32>)
305 return SettingType::kU32;
306 else if constexpr (std::is_same_v<T, u64>)
307 return SettingType::kU64;
308 else if constexpr (std::is_same_v<T, f64>)
309 return SettingType::kF64;
310 else if constexpr (std::is_same_v<T, std::string>)
311 return SettingType::kString;
312 else
313 return SettingType::kPath;
314}
315
316inline void ApplyTrait(SettingConstraints& c, const Range& r) {
317 c.min = r.min;
318 c.max = r.max;
319}
320inline void ApplyTrait(SettingConstraints& c, const Allowed& a) {
321 c.allowed = a.values;
322}
323inline void ApplyTrait(SettingConstraints& c, SettingLife life) {
324 c.life = life;
325}
326inline void ApplyTrait(SettingConstraints& c, NoSave) {
327 c.no_save = true;
328}
329inline void ApplyTrait(SettingConstraints& c, DebugOnly) {
330 c.debug_only = true;
331}
332inline void ApplyTrait(SettingConstraints& c, KeyChordTrait) {
333 c.key_chord = true;
334}
335
336} // namespace detail
337
338template <typename T>
339template <typename... Traits>
340Setting<T>::Setting(SettingOwner* owner, std::string_view name, const T& default_value,
341 std::string_view help, Traits... traits)
342 : SettingBase(*owner, name, detail::TypeOf<T>(), help),
343 default_(default_value),
344 staged_(default_value),
345 active_(default_value) {
346 (detail::ApplyTrait(EditConstraints(), traits), ...);
347}
348
349template <typename T>
350T Setting<T>::GetActive() const {
351 if constexpr (kScalar) {
352 return active_.load(std::memory_order_relaxed);
353 } else {
354 std::lock_guard lock(mutex_);
355 return active_;
356 }
357}
358
359template <typename T>
361 if constexpr (std::is_same_v<T, std::filesystem::path>) {
362 return detail::ResolveSettingPath(GetActive());
363 } else {
364 return GetActive();
365 }
366}
367
368template <typename T>
370 return SetFromCode(detail::RenderSetting(value));
371}
372
373template <typename T>
375 return SetText(SettingLayer::kLock, detail::RenderSetting(value));
376}
377
378template <typename T>
380 u64 id;
381 {
382 std::lock_guard lock(mutex_);
383 id = next_watch_++;
384 watches_.emplace_back(id, std::make_shared<MoveOnlyFunction<void(const T&)>>(std::move(fn)));
385 }
386 return ScopeGuard([this, id] {
387 std::lock_guard lock(mutex_);
388 std::erase_if(watches_, [id](const auto& entry) { return entry.first == id; });
389 });
390}
391
392template <typename T>
393bool Setting<T>::Store(std::string_view text) {
394 T parsed{};
395 if (!detail::ParseSetting(text, parsed)) {
396 return false;
397 }
398 std::lock_guard lock(mutex_);
399 staged_ = std::move(parsed);
400 return true;
401}
402
403template <typename T>
404void Setting<T>::Publish() {
405 std::lock_guard lock(mutex_);
406 if constexpr (kScalar) {
407 active_.store(staged_, std::memory_order_relaxed);
408 } else {
409 active_ = staged_;
410 }
411}
412
413template <typename T>
414std::string Setting<T>::Render() const {
415 std::lock_guard lock(mutex_);
416 return detail::RenderSetting(staged_);
417}
418
419template <typename T>
420std::string Setting<T>::RenderDefault() const {
421 return detail::RenderSetting(default_);
422}
423
424template <typename T>
425void Setting<T>::Notify() {
426 std::vector<std::shared_ptr<MoveOnlyFunction<void(const T&)>>> calls;
427 T value = GetActive();
428 {
429 std::lock_guard lock(mutex_);
430 for (auto& entry : watches_) {
431 calls.push_back(entry.second);
432 }
433 }
434 for (auto& call : calls) {
435 (*call)(value);
436 }
437}
438} // namespace rex
439
451#define REX_SETTING(Type, name, default_value, help, ...) \
452 ::rex::Setting<Type> name { \
453 this, #name, default_value, help __VA_OPT__(, ) __VA_ARGS__ \
454 }
Definition console_command.h:26
Definition utility.h:305
bool IsFrozen() const
Result< void > Reset()
SettingLayer GetLayer() const
Result< void > SetFromCode(std::string_view text)
bool IsRestartPending() const
std::string GetText() const
Result< void > SetText(SettingLayer layer, std::string_view text)
SettingConstraints & EditConstraints()
Result< void > Check(std::string_view text) const
const SettingConstraints & GetConstraints() const
const std::string & GetName() const
Definition setting.h:249
std::span< SettingBase *const > GetOwnedSettings() const
SettingOwner(std::string_view scope)
virtual ~SettingOwner()
Result< void > Set(const T &value)
Definition setting.h:369
ScopeGuard OnChange(MoveOnlyFunction< void(const T &)> fn)
Definition setting.h:379
Setting(SettingOwner *owner, std::string_view name, const T &default_value, std::string_view help, Traits... traits)
Definition setting.h:340
Result< void > Lock(const T &value)
Definition setting.h:374
T Get() const
Definition setting.h:360
Definition settings_registry.h:139
Interfaces, factory and module declaration.
Definition app.h:36
SettingLife
Definition setting.h:60
@ kRestart
Definition setting.h:62
@ kLive
Definition setting.h:61
@ kStartupOnly
Definition setting.h:63
SettingType
Definition setting.h:38
@ kPath
Definition setting.h:46
typename detail::MoveOnlyFunctionFor< Signature >::type MoveOnlyFunction
Definition utility.h:254
std::expected< T, Error > Result
Definition result.h:98
SettingLayer
Definition setting.h:50
@ kEnvironment
Definition setting.h:54
@ kCommandLine
Definition setting.h:55
@ kLock
Definition setting.h:57
Platform flags, packing macros and export markers.
#define REX_API
Definition platform.h:177
Definition setting.h:96
Definition setting.h:98
Definition setting.h:94
Definition setting.h:78
Definition setting.h:67
bool debug_only
Definition setting.h:73
bool key_chord
Definition setting.h:74
std::vector< std::string > allowed
Definition setting.h:70
bool no_save
Definition setting.h:72
Integer types, byte order wrappers and size literals.
Move only function, const propagating pointer and scope guard.