Thread System 0.3.1
High-performance C++20 thread pool with work stealing and DAG scheduling
Loading...
Searching...
No Matches
configuration_manager.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
11#pragma once
12
13#include <any>
14#include <filesystem>
15#include <fstream>
16#include <functional>
17#include <memory>
18#include <mutex>
19#include <sstream>
20#include <string>
21#include <unordered_map>
22#include <variant>
23#include <vector>
25
26namespace kcenon::thread {
27
31using config_value = std::variant<
32 bool,
33 int,
34 double,
35 std::string,
36 std::vector<std::string>,
37 std::unordered_map<std::string, std::any>
38>;
39
44 bool is_valid{true};
45 std::vector<std::string> errors;
46 std::vector<std::string> warnings;
47
48 void add_error(const std::string& error) {
49 errors.push_back(error);
50 is_valid = false;
51 }
52
53 void add_warning(const std::string& warning) {
54 warnings.push_back(warning);
55 }
56};
57
62public:
66 using change_callback = std::function<void(const std::string&, const config_value&)>;
67
71 using validator_func = std::function<validation_result(const std::string&, const config_value&)>;
72
77 explicit configuration_manager(std::shared_ptr<event_bus> bus = nullptr)
78 : event_bus_(bus) {
79 if (!event_bus_) {
80 event_bus_ = std::make_shared<event_bus>();
81 }
82 }
83
89 bool load_from_file(const std::filesystem::path& config_file) {
90 if (!std::filesystem::exists(config_file)) {
91 return false;
92 }
93
94 try {
95 std::ifstream file(config_file);
96 if (!file.is_open()) {
97 return false;
98 }
99
100 // Simple key=value parser for demonstration
101 // In production, use JSON/YAML parser
102 std::string line;
103 while (std::getline(file, line)) {
104 parse_config_line(line);
105 }
106
107 return true;
108 } catch (...) {
109 return false;
110 }
111 }
112
118 bool save_to_file(const std::filesystem::path& config_file) const {
119 try {
120 std::ofstream file(config_file);
121 if (!file.is_open()) {
122 return false;
123 }
124
125 std::lock_guard<std::mutex> lock(mutex_);
126 for (const auto& [key, value] : config_) {
127 file << key << "=" << value_to_string(value) << "\n";
128 }
129
130 return true;
131 } catch (...) {
132 return false;
133 }
134 }
135
142 bool set(const std::string& path, const config_value& value) {
143 // Validate if validator exists
144 if (auto it = validators_.find(path); it != validators_.end()) {
145 auto result = it->second(path, value);
146 if (!result.is_valid) {
147 return false;
148 }
149 }
150
151 config_value old_value;
152 bool had_value = false;
153
154 {
155 std::lock_guard<std::mutex> lock(mutex_);
156 if (auto it = config_.find(path); it != config_.end()) {
157 old_value = it->second;
158 had_value = true;
159 }
160 config_[path] = value;
161 }
162
163 // Notify callbacks
164 notify_change(path, value);
165
166 // Publish event if value changed
167 if (event_bus_ && (!had_value || !values_equal(old_value, value))) {
168 event_bus_->publish(config_changed_event(path, old_value, value));
169 }
170
171 return true;
172 }
173
181 template<typename T>
182 T get(const std::string& path, const T& default_value = T{}) const {
183 std::lock_guard<std::mutex> lock(mutex_);
184 auto it = config_.find(path);
185 if (it != config_.end()) {
186 try {
187 return std::get<T>(it->second);
188 } catch (const std::bad_variant_access&) {
189 // Type mismatch
190 }
191 }
192 return default_value;
193 }
194
201 template<typename T>
202 std::optional<T> get_optional(const std::string& path) const {
203 std::lock_guard<std::mutex> lock(mutex_);
204 auto it = config_.find(path);
205 if (it != config_.end()) {
206 try {
207 return std::get<T>(it->second);
208 } catch (const std::bad_variant_access&) {
209 // Type mismatch
210 }
211 }
212 return std::nullopt;
213 }
214
220 bool has(const std::string& path) const {
221 std::lock_guard<std::mutex> lock(mutex_);
222 return config_.find(path) != config_.end();
223 }
224
230 bool remove(const std::string& path) {
231 std::lock_guard<std::mutex> lock(mutex_);
232 return config_.erase(path) > 0;
233 }
234
241 std::size_t on_change(const std::string& path, change_callback callback) {
242 std::lock_guard<std::mutex> lock(callbacks_mutex_);
243 auto id = next_callback_id_++;
244 callbacks_[path][id] = std::move(callback);
245 return id;
246 }
247
253 void remove_callback(const std::string& path, std::size_t id) {
254 std::lock_guard<std::mutex> lock(callbacks_mutex_);
255 if (auto it = callbacks_.find(path); it != callbacks_.end()) {
256 it->second.erase(id);
257 if (it->second.empty()) {
258 callbacks_.erase(it);
259 }
260 }
261 }
262
268 void add_validator(const std::string& path, validator_func validator) {
269 std::lock_guard<std::mutex> lock(mutex_);
270 validators_[path] = std::move(validator);
271 }
272
279 std::lock_guard<std::mutex> lock(mutex_);
280
281 for (const auto& [path, value] : config_) {
282 if (auto it = validators_.find(path); it != validators_.end()) {
283 auto path_result = it->second(path, value);
284 if (!path_result.is_valid) {
285 result.is_valid = false;
286 for (const auto& error : path_result.errors) {
287 result.add_error(path + ": " + error);
288 }
289 }
290 for (const auto& warning : path_result.warnings) {
291 result.add_warning(path + ": " + warning);
292 }
293 }
294 }
295
296 return result;
297 }
298
304 void apply_system_config(const std::string& system_name,
305 const std::unordered_map<std::string, config_value>& config) {
306 for (const auto& [key, value] : config) {
307 set(system_name + "." + key, value);
308 }
309 }
310
316 std::unordered_map<std::string, config_value> get_system_config(
317 const std::string& system_name) const {
318 std::unordered_map<std::string, config_value> result;
319 std::string prefix = system_name + ".";
320
321 std::lock_guard<std::mutex> lock(mutex_);
322 for (const auto& [key, value] : config_) {
323 if (key.starts_with(prefix)) {
324 result[key.substr(prefix.length())] = value;
325 }
326 }
327
328 return result;
329 }
330
334 void clear() {
335 std::lock_guard<std::mutex> lock(mutex_);
336 config_.clear();
337 }
338
345 return instance;
346 }
347
348private:
352 void parse_config_line(const std::string& line) {
353 if (line.empty() || line[0] == '#' || line[0] == ';') {
354 return; // Skip comments and empty lines
355 }
356
357 auto pos = line.find('=');
358 if (pos != std::string::npos) {
359 std::string key = line.substr(0, pos);
360 std::string value_str = line.substr(pos + 1);
361
362 // Trim whitespace
363 key.erase(0, key.find_first_not_of(" \t"));
364 key.erase(key.find_last_not_of(" \t") + 1);
365 value_str.erase(0, value_str.find_first_not_of(" \t"));
366 value_str.erase(value_str.find_last_not_of(" \t") + 1);
367
368 // Parse value type
369 config_value value;
370 if (value_str == "true" || value_str == "false") {
371 value = (value_str == "true");
372 } else if (std::all_of(value_str.begin(), value_str.end(), ::isdigit)) {
373 value = std::stoi(value_str);
374 } else {
375 try {
376 value = std::stod(value_str);
377 } catch (...) {
378 value = value_str; // Default to string
379 }
380 }
381
382 set(key, value);
383 }
384 }
385
389 std::string value_to_string(const config_value& value) const {
390 return std::visit([](const auto& v) -> std::string {
391 using T = std::decay_t<decltype(v)>;
392 if constexpr (std::is_same_v<T, bool>) {
393 return v ? "true" : "false";
394 } else if constexpr (std::is_same_v<T, std::string>) {
395 return v;
396 } else if constexpr (std::is_arithmetic_v<T>) {
397 return std::to_string(v);
398 } else {
399 return ""; // Complex types need custom serialization
400 }
401 }, value);
402 }
403
410 bool values_equal(const config_value& a, const config_value& b) const {
411 if (a.index() != b.index()) {
412 return false;
413 }
414 return std::visit([&b](const auto& val_a) -> bool {
415 using T = std::decay_t<decltype(val_a)>;
416 const auto& val_b = std::get<T>(b);
417 if constexpr (std::is_same_v<T, std::unordered_map<std::string, std::any>>) {
418 return false; // std::any has no operator==
419 } else {
420 return val_a == val_b;
421 }
422 }, a);
423 }
424
428 void notify_change(const std::string& path, const config_value& value) {
429 std::lock_guard<std::mutex> lock(callbacks_mutex_);
430
431 // Notify specific path callbacks
432 if (auto it = callbacks_.find(path); it != callbacks_.end()) {
433 for (const auto& [id, callback] : it->second) {
434 callback(path, value);
435 }
436 }
437
438 // Notify global callbacks
439 if (auto it = callbacks_.find(""); it != callbacks_.end()) {
440 for (const auto& [id, callback] : it->second) {
441 callback(path, value);
442 }
443 }
444 }
445
446 mutable std::mutex mutex_;
447 mutable std::mutex callbacks_mutex_;
448 std::unordered_map<std::string, config_value> config_;
449 std::unordered_map<std::string, validator_func> validators_;
450 std::unordered_map<std::string, std::unordered_map<std::size_t, change_callback>> callbacks_;
451 std::shared_ptr<event_bus> event_bus_;
452 std::size_t next_callback_id_{1};
453};
454
455} // namespace kcenon::thread
Configuration manager for unified system configuration.
std::string value_to_string(const config_value &value) const
Convert value to string.
void notify_change(const std::string &path, const config_value &value)
Notify change callbacks.
std::unordered_map< std::string, std::unordered_map< std::size_t, change_callback > > callbacks_
void remove_callback(const std::string &path, std::size_t id)
Unregister a change callback.
configuration_manager(std::shared_ptr< event_bus > bus=nullptr)
Constructor.
bool save_to_file(const std::filesystem::path &config_file) const
Save configuration to file.
std::function< validation_result(const std::string &, const config_value &)> validator_func
Configuration validator.
void parse_config_line(const std::string &line)
Parse a configuration line.
std::unordered_map< std::string, config_value > get_system_config(const std::string &system_name) const
Get configuration for a specific system.
bool remove(const std::string &path)
Remove a configuration value.
std::optional< T > get_optional(const std::string &path) const
Get a configuration value as optional.
validation_result validate_all() const
Validate all configuration.
void add_validator(const std::string &path, validator_func validator)
Register a configuration validator.
void apply_system_config(const std::string &system_name, const std::unordered_map< std::string, config_value > &config)
Apply configuration for a specific system.
bool values_equal(const config_value &a, const config_value &b) const
Check if two values are equal.
std::function< void(const std::string &, const config_value &)> change_callback
Configuration change callback.
bool load_from_file(const std::filesystem::path &config_file)
Load configuration from file.
bool set(const std::string &path, const config_value &value)
Set a configuration value.
std::size_t on_change(const std::string &path, change_callback callback)
Register a change callback.
bool has(const std::string &path) const
Check if configuration exists.
std::unordered_map< std::string, config_value > config_
std::unordered_map< std::string, validator_func > validators_
T get(const std::string &path, const T &default_value=T{}) const
Get a configuration value.
static configuration_manager & instance()
Get singleton instance.
Represents an error in the thread system.
A template class representing either a value or an error.
Type-safe publish-subscribe event bus for thread system events.
@ callback
Call user callback for custom decision.
Core threading foundation of the thread system library.
Definition thread_impl.h:17
std::variant< bool, int, double, std::string, std::vector< std::string >, std::unordered_map< std::string, std::any > > config_value
Configuration value type.
Configuration changed event.
Definition event_bus.h:314
Configuration validation result.
void add_warning(const std::string &warning)
void add_error(const std::string &error)
std::vector< std::string > warnings