跳转至

ConfigStore 最佳实践

文档信息

项目 内容
文档版本 v1.0
创建日期 2026-03-17
所属模块 cf::config (ConfigStore)
前置知识 快速入门指南 (01-quick-start.md)

一、键命名规范

1.1 分层结构建议

ConfigStore 使用点分隔的分层键名结构,建议采用 2-4 层的命名深度:

[level1].[level2].[level3].[level4]
   |        |        |        |
   |        |        |        +-- 具体属性名 (必需)
   |        |        +----------- 子模块 (可选)
   |        +-------------------- 功能模块 (必需)
   +----------------------------- 命名空间/域 (必需)

推荐示例:

app.theme.name              # 应用主题名称
app.theme.dark_mode         # 主题深色模式
ui.window.width             # 窗口宽度
ui.window.height            # 窗口高度
user.preferences.language   # 用户语言偏好
network.proxy.host          # 代理主机地址
network.proxy.port          # 代理端口
editor.font.size            # 编辑器字体大小
editor.font.family          # 编辑器字体族

1.2 命名约定

遵循以下命名约定以确保代码一致性:

规则 说明 示例
小写字母 所有层级使用小写字母 app.theme.name
点分隔 使用 . 作为层级分隔符 ui.window.width
语义清晰 使用描述性名称,避免缩写 max_file_size 而非 mfs
下划线连接 多词属性用 _ 连接 dark_mode 而非 darkMode
避免数字 除非是版本或编号 schema_v2 而非 style1
全英文 键名使用英文,非中文 background_color 而非 背景颜色

1.3 避免冲突的建议

为避免键名冲突,推荐为不同模块或组件使用命名空间前缀:

# 为每个子系统预留独立的命名空间
app.*          # 应用程序级配置
ui.*           # 用户界面配置
editor.*       # 编辑器配置
network.*      # 网络配置
database.*     # 数据库配置
logger.*       # 日志配置

常见命名空间:

// 在代码中定义命名空间常量
namespace ConfigKeys {
    constexpr std::string_view APP_PREFIX = "app";
    constexpr std::string_view UI_PREFIX = "ui";
    constexpr std::string_view EDITOR_PREFIX = "editor";

    // 预定义的 KeyView
    inline constexpr KeyView APP_THEME_NAME{.group = "app.theme", .key = "name"};
    inline constexpr KeyView UI_WINDOW_WIDTH{.group = "ui.window", .key = "width"};
    inline constexpr KeyView EDITOR_FONT_SIZE{.group = "editor.font", .key = "size"};
}

二、层级使用策略

ConfigStore 提供四层存储架构,每一层都有其特定的使用场景:

2.1 层级优先级图

+-----------------------------+
|         Temp 层 (优先级 3)   |  内存临时数据,进程重启后丢失
+-----------------------------+
                    |
                    v 覆盖
+-----------------------------+
|         App 层 (优先级 2)    |  应用运行时配置,会话有效
+-----------------------------+
                    |
                    v 覆盖
+-----------------------------+
|         User 层 (优先级 1)   |  用户个人偏好,跨会话持久化
+-----------------------------+
                    |
                    v 覆盖
+-----------------------------+
|       System 层 (优先级 0)   |  系统默认配置,管理员维护
+-----------------------------+

2.2 System 层:系统默认

用途: 存储应用程序的系统默认配置,由管理员或安装程序维护。

使用场景: - 默认配置值 - 系统级限制(如最大文件大小) - 硬件相关的默认设置 - 部署环境配置

示例:

// 初始化系统默认配置 (通常在安装时或首次运行时设置)
void init_system_defaults() {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    // 系统默认配置应该设置为只读参考
    store.register_key(
        Key{.full_key = "app.version", .full_description = "Application version"},
        std::string("1.0.0"),
        Layer::System
    );

    store.register_key(
        Key{.full_key = "file.max_size_mb", .full_description = "Max file size in MB"},
        100,
        Layer::System
    );

    store.register_key(
        Key{.full_key = "performance.default_dpi", .full_description = "Default DPI setting"},
        96,
        Layer::System
    );

    store.sync(SyncMethod::Sync);  // 同步写入
}

2.3 User 层:用户偏好

用途: 存储用户的个人偏好设置,在用户配置文件中持久化。

使用场景: - 主题和外观设置 - 语言和区域设置 - 用户习惯(窗口大小、位置) - 功能开关

示例:

// 保存用户偏好设置
void save_user_preferences(const UserPreferences& prefs) {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    store.set(KeyView{.group = "user.theme", .key = "name"},
              prefs.theme_name, Layer::User);
    store.set(KeyView{.group = "user.theme", .key = "dark_mode"},
              prefs.dark_mode, Layer::User);
    store.set(KeyView{.group = "user.interface", .key = "language"},
              prefs.language, Layer::User);
    store.set(KeyView{.group = "user.interface", .key = "font_size"},
              prefs.font_size, Layer::User);

    // 异步保存,不阻塞 UI
    store.sync(SyncMethod::Async);
}

// 加载用户偏好设置
UserPreferences load_user_preferences() {
    using namespace cf::config;
    auto& store = ConfigStore::instance();
    UserPreferences prefs;

    prefs.theme_name = store.query<std::string>(
        KeyView{.group = "user.theme", .key = "name"}, "default");
    prefs.dark_mode = store.query<bool>(
        KeyView{.group = "user.theme", .key = "dark_mode"}, false);
    prefs.language = store.query<std::string>(
        KeyView{.group = "user.interface", .key = "language"}, "en_US");
    prefs.font_size = store.query<int>(
        KeyView{.group = "user.interface", .key = "font_size"}, 12);

    return prefs;
}

2.4 App 层:运行时状态

用途: 存储应用程序运行时状态,与应用程序生命周期绑定。

使用场景: - 窗口几何状态 - 最近打开的文件列表 - 会话恢复数据 - 应用程序内部状态

示例:

// 保存窗口状态
void save_window_state(QWidget* window) {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    auto geometry = window->geometry();
    store.set(KeyView{.group = "app.window", .key = "x"},
              geometry.x(), Layer::App);
    store.set(KeyView{.group = "app.window", .key = "y"},
              geometry.y(), Layer::App);
    store.set(KeyView{.group = "app.window", .key = "width"},
              geometry.width(), Layer::App);
    store.set(KeyView{.group = "app.window", .key = "height"},
              geometry.height(), Layer::App);
    store.set(KeyView{.group = "app.window", .key = "maximized"},
              window->isMaximized(), Layer::App);

    store.sync(SyncMethod::Async);
}

// 恢复窗口状态
void restore_window_state(QWidget* window) {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    int x = store.query<int>(KeyView{.group = "app.window", .key = "x"}, 100);
    int y = store.query<int>(KeyView{.group = "app.window", .key = "y"}, 100);
    int w = store.query<int>(KeyView{.group = "app.window", .key = "width"}, 800);
    int h = store.query<int>(KeyView{.group = "app.window", .key = "height"}, 600);
    bool maximized = store.query<bool>(
        KeyView{.group = "app.window", .key = "maximized"}, false);

    window->setGeometry(x, y, w, h);
    if (maximized) {
        window->showMaximized();
    }
}

2.5 Temp 层:临时数据

用途: 存储进程生命周期内的临时数据,不持久化到磁盘。

使用场景: - 单元测试中的临时配置 - 功能演示/预览模式 - 会话令牌 - 调试标志

示例:

// 使用 Temp 层进行单元测试
TEST(ConfigStoreTest, OverrideWithTempLayer) {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    // 设置系统默认值
    store.set(KeyView{.group = "test", .key = "value"},
              "system_default", Layer::System);

    // 使用 Temp 层覆盖 (不持久化)
    store.set(KeyView{.group = "test", .key = "value"},
              "temp_override", Layer::Temp);

    // 查询返回 Temp 层的值
    auto value = store.query<std::string>(
        KeyView{.group = "test", .key = "value"});
    EXPECT_EQ(value, "temp_override");

    // 清除 Temp 层后,回退到 System 层
    store.clear_layer(Layer::Temp);
    value = store.query<std::string>(
        KeyView{.group = "test", .key = "value"});
    EXPECT_EQ(value, "system_default");
}

// 临时启用调试模式
void enable_debug_mode_temporarily() {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    store.set(KeyView{.group = "debug", .key = "enabled"},
              true, Layer::Temp);
    store.set(KeyView{.group = "debug", .key = "log_level"},
              5, Layer::Temp);

    // 不会持久化到磁盘
}

2.6 层级选择决策表

场景 推荐层级 持久化 谁可修改
应用默认值 System 安装程序/管理员
用户偏好 User 最终用户
窗口状态 App 应用程序
会话令牌 Temp 应用程序
测试数据 Temp 测试代码
限制配置 System 管理员
功能开关 User 最终用户

三、性能优化

3.1 缓存利用技巧

ConfigStore 在内存中维护缓存,遵循以下最佳实践以充分利用缓存:

1. 优先使用优先级查询

// 推荐:让 ConfigStore 自动查找最高优先级的值
auto value = store.query<int>(KeyView{.group = "app", .key = "timeout"}, 5000);

// 不推荐:手动查询各层
int value = 5000;
if (auto v = store.query<int>(KeyView{.group = "app", .key = "timeout"}, Layer::Temp)) {
    value = *v;
} else if (auto v = store.query<int>(KeyView{.group = "app", .key = "timeout"}, Layer::App)) {
    value = *v;
} // ...

2. 批量读取配置

// 推荐:一次性读取需要的配置
struct AppSettings {
    std::string theme;
    int dpi;
    bool dark_mode;
    double scale;

    static AppSettings load() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();
        return AppSettings{
            .theme = store.query<std::string>(
                KeyView{.group = "app.theme", .key = "name"}, "default"),
            .dpi = store.query<int>(
                KeyView{.group = "app.display", .key = "dpi"}, 96),
            .dark_mode = store.query<bool>(
                KeyView{.group = "app.theme", .key = "dark_mode"}, false),
            .scale = store.query<double>(
                KeyView{.group = "app.display", .key = "scale"}, 1.0)
        };
    }
};

// 使用缓存的配置
auto settings = AppSettings::load();
apply_settings(settings);

3. 避免频繁查询同一配置

// 不推荐:每次函数调用都查询
void render_frame() {
    auto scale = ConfigStore::instance().query<double>(
        KeyView{.group = "display", .key = "scale"}, 1.0);
    // 使用 scale...
}

// 推荐:缓存配置值,监听变化
class Renderer {
public:
    Renderer() {
        // 初始化时读取
        scale_ = ConfigStore::instance().query<double>(
            KeyView{.group = "display", .key = "scale"}, 1.0);

        // 监听变化
        watcher_handle_ = ConfigStore::instance().watch(
            "display.scale",
            [this](const Key&, const std::any*, const std::any* new_val, Layer) {
                if (new_val) {
                    scale_ = std::any_cast<double>(*new_val);
                    on_scale_changed();
                }
            }
        );
    }

    ~Renderer() {
        ConfigStore::instance().unwatch(watcher_handle_);
    }

    void render_frame() {
        // 直接使用缓存的值
        double scale = scale_;
        // ...
    }

private:
    double scale_;
    WatcherHandle watcher_handle_;

    void on_scale_changed() {
        // 处理缩放变化
    }
};

3.2 批量操作建议

当需要修改多个配置项时,使用手动通知策略可以批量处理变更:

// 批量修改配置
void update_multiple_settings() {
    using namespace cf::config;
    auto& store = ConfigStore::instance();

    // 使用 Manual 策略,避免每次 set 都触发 Watcher
    NotifyPolicy manual = NotifyPolicy::Manual;

    store.set(KeyView{.group = "ui", .key = "theme"}, "dark", Layer::User, manual);
    store.set(KeyView{.group = "ui", .key = "font_size"}, 14, Layer::User, manual);
    store.set(KeyView{.group = "ui", .key = "scale"}, 1.25, Layer::User, manual);

    // 一次性触发所有 Watcher
    store.notify();

    // 一次性同步到磁盘
    store.sync(SyncMethod::Async);
}

3.3 异步持久化的使用

对于频繁写入的配置,使用异步持久化避免阻塞主线程:

// 异步保存配置示例
class ConfigSaver {
public:
    static ConfigSaver& instance() {
        static ConfigSaver instance;
        return instance;
    }

    void schedule_save() {
        if (!save_pending_.load()) {
            save_pending_.store(true);
            // 延迟 500ms 后保存,避免频繁 I/O
            QTimer::singleShot(500, [this]() {
                if (save_pending_.load()) {
                    ConfigStore::instance().sync(SyncMethod::Async);
                    save_pending_.store(false);
                }
            });
        }
    }

    void save_immediately() {
        ConfigStore::instance().sync(SyncMethod::Sync);
        save_pending_.store(false);
    }

private:
    ConfigSaver() = default;
    std::atomic<bool> save_pending_{false};
};

// 使用示例
void on_config_changed() {
    ConfigStore::instance().set(...);
    ConfigSaver::instance().schedule_save();  // 延迟异步保存
}

void on_application_exit() {
    ConfigSaver::instance().save_immediately();  // 退出时立即保存
}

四、线程安全

4.1 读多写少场景的最佳实践

ConfigStore 使用 std::shared_mutex 实现读写锁,非常适合读多写少的场景:

// 多线程读取配置 - 完全并发
class ConfigReader {
public:
    int get_timeout() const {
        // query() 内部使用 shared_lock,允许多个线程并发读取
        return ConfigStore::instance().query<int>(
            KeyView{.group = "network", .key = "timeout"}, 5000);
    }

    std::string get_server_url() const {
        return ConfigStore::instance().query<std::string>(
            KeyView{.group = "network", .key = "server_url"}, "localhost");
    }
};

// 多线程安全使用
void worker_thread(int id) {
    ConfigReader reader;
    for (int i = 0; i < 1000; ++i) {
        int timeout = reader.get_timeout();
        std::string url = reader.get_server_url();
        // 使用配置...
    }
}

4.2 避免死锁的注意事项

1. 不要在 Watcher 回调中调用 ConfigStore::set()

// 危险:可能导致死锁
auto handle = ConfigStore::instance().watch(
    "app.theme",
    [](const Key&, auto, auto, Layer) {
        // 危险!在回调中再次写入可能导致死锁
        ConfigStore::instance().set(KeyView{.group = "app", .key = "changed"}, true);
    }
);

// 安全:使用标志位延迟处理
std::atomic<bool> theme_changed{false};
auto handle = ConfigStore::instance().watch(
    "app.theme",
    [&theme_changed](const Key&, auto, auto, Layer) {
        theme_changed.store(true);
    }
);

// 在主循环中处理
void main_loop() {
    if (theme_changed.load()) {
        theme_changed.store(false);
        // 安全地处理主题变更
        ConfigStore::instance().set(KeyView{.group = "app", .key = "changed"}, true);
    }
}

2. 持有其他锁时避免调用 ConfigStore

class MyClass {
    std::mutex data_mutex_;
    SomeData data_;

    void update_config() {
        // 不推荐:在持有 data_mutex_ 时调用 ConfigStore
        std::lock_guard lock(data_mutex_);
        ConfigStore::instance().set(...);  // 可能导致死锁
    }

    // 推荐:分离锁的范围
    void update_config_safe() {
        // 先准备数据
        SomeData new_data;
        {
            std::lock_guard lock(data_mutex_);
            new_data = data_;
        }
        // 释放锁后再调用 ConfigStore
        ConfigStore::instance().set(...);
    }
};

4.3 Watcher 回调中的注意事项

// Watcher 回调的最佳实践
class SafeWatcher {
public:
    void start_watching() {
        handle_ = ConfigStore::instance().watch(
            "app.*",
            [this](const Key& k, const std::any* old_val,
                   const std::any* new_val, Layer layer) {
                // 1. 避免在回调中执行耗时操作
                // 2. 不要在回调中直接修改 ConfigStore
                // 3. 使用线程安全的方式通知主线程

                // 提取必要的信息
                std::string key = k.full_key;
                std::any new_value = new_val ? *new_val : std::any();

                // 将事件放入队列,由主线程处理
                std::lock_guard lock(queue_mutex_);
                event_queue_.push({key, new_value, layer});
                cv_.notify_one();
            }
        );

        // 启动处理线程
        worker_thread_ = std::thread(&SafeWatcher::process_events, this);
    }

    ~SafeWatcher() {
        {
            std::lock_guard lock(queue_mutex_);
            stop_ = true;
        }
        cv_.notify_one();
        if (worker_thread_.joinable()) {
            worker_thread_.join();
        }
        ConfigStore::instance().unwatch(handle_);
    }

private:
    struct Event {
        std::string key;
        std::any value;
        Layer layer;
    };

    void process_events() {
        while (true) {
            std::unique_lock lock(queue_mutex_);
            cv_.wait(lock, [this] {
                return stop_ || !event_queue_.empty();
            });

            if (stop_) break;

            while (!event_queue_.empty()) {
                auto event = event_queue_.front();
                event_queue_.pop();
                lock.unlock();

                // 处理事件 (不在锁内)
                handle_event(event);

                lock.lock();
            }
        }
    }

    void handle_event(const Event& event) {
        // 安全地处理配置变更
        // 可以在这里安全地读取 ConfigStore
    }

    WatcherHandle handle_;
    std::thread worker_thread_;
    std::mutex queue_mutex_;
    std::condition_variable cv_;
    std::queue<Event> event_queue_;
    bool stop_ = false;
};

五、错误处理

5.1 类型转换的处理

ConfigStore 使用 std::anyQVariant 存储值,类型转换可能失败:

// 安全的类型转换处理
class SafeConfigReader {
public:
    // 方法 1:使用默认值
    int get_int(KeyView kv, int default_value = 0) {
        try {
            return ConfigStore::instance().query<int>(kv, default_value);
        } catch (const std::exception& e) {
            log_error("Failed to read int config: ", e.what());
            return default_value;
        }
    }

    // 方法 2:使用 optional 检查
    std::optional<double> get_double(KeyView kv) {
        auto value = ConfigStore::instance().query<double>(kv);
        if (value.has_value()) {
            return value;
        }
        // 尝试从字符串转换
        auto str_value = ConfigStore::instance().query<std::string>(kv);
        if (str_value) {
            try {
                return std::stod(*str_value);
            } catch (...) {
                return std::nullopt;
            }
        }
        return std::nullopt;
    }

    // 方法 3:验证范围
    int get_percentage(KeyView kv) {
        int value = ConfigStore::instance().query<int>(kv, 100);
        // 限制在 0-100 范围
        if (value < 0) return 0;
        if (value > 100) return 100;
        return value;
    }
};

5.2 键验证的处理

// 键名验证
class ConfigValidator {
public:
    static bool is_valid_key(const std::string& key) {
        // 检查键名格式
        if (key.empty() || key.length() > 256) {
            return false;
        }

        // 检查只包含允许的字符
        for (char c : key) {
            if (!(std::isalnum(c) || c == '_' || c == '.')) {
                return false;
            }
        }

        return true;
    }

    static bool set_safe(const KeyView& kv, const std::string& value) {
        // 先验证键名
        KeyHelper helper;
        Key k;
        if (!helper.fromKeyViewToKey(kv, k)) {
            return false;
        }

        if (!is_valid_key(k.full_key)) {
            return false;
        }

        // 再执行设置
        return ConfigStore::instance().set(kv, value);
    }
};

5.3 优雅降级策略

// 配置加载的优雅降级
class RobustConfigLoader {
public:
    struct Config {
        std::string server_url;
        int timeout;
        int retry_count;
        bool enable_cache;

        static Config load_with_fallbacks() {
            using namespace cf::config;
            auto& store = ConfigStore::instance();

            Config config;

            // 尝试多个可能的键名
            config.server_url = store.query<std::string>(
                KeyView{.group = "network", .key = "server_url"},
                // 尝试备用键名
                store.query<std::string>(
                    KeyView{.group = "network", .key = "host"},
                    // 最后使用硬编码默认值
                    "localhost"
                )
            );

            // 带验证的加载
            config.timeout = load_int_with_validation(
                KeyView{.group = "network", .key = "timeout"},
                1000,  // 默认值
                100,   // 最小值
                60000  // 最大值
            );

            config.retry_count = load_int_with_validation(
                KeyView{.group = "network", .key = "retry_count"},
                3,  // 默认值
                0,  // 最小值
                10  // 最大值
            );

            config.enable_cache = store.query<bool>(
                KeyView{.group = "network", .key = "enable_cache"},
                true  // 默认启用
            );

            return config;
        }

    private:
        static int load_int_with_validation(KeyView kv, int default_value,
                                            int min_value, int max_value) {
            using namespace cf::config;
            auto& store = ConfigStore::instance();

            int value = store.query<int>(kv, default_value);
            if (value < min_value) {
                log_warning("Config value too low, using minimum: ", min_value);
                return min_value;
            }
            if (value > max_value) {
                log_warning("Config value too high, using maximum: ", max_value);
                return max_value;
            }
            return value;
        }

        static void log_warning(const std::string& msg, int value) {
            // 实现日志记录
        }
    };
};

六、实践模式

6.1 模式一:配置管理器封装类

封装 ConfigStore 以提供类型安全的配置访问接口:

/**
 * @brief 应用程序配置管理器
 *
 * 封装 ConfigStore,提供类型安全的配置访问接口,
 * 并集中管理配置键名和默认值。
 */
class AppConfigManager {
public:
    // 单例模式
    static AppConfigManager& instance() {
        static AppConfigManager instance;
        return instance;
    }

    // ========== 主题配置 ==========

    struct ThemeConfig {
        std::string name = "default";
        bool dark_mode = false;
        int accent_color = 0x2196F3;
    };

    ThemeConfig get_theme() const {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        return ThemeConfig{
            .name = store.query<std::string>(
                KeyView{.group = "app.theme", .key = "name"}, "default"),
            .dark_mode = store.query<bool>(
                KeyView{.group = "app.theme", .key = "dark_mode"}, false),
            .accent_color = store.query<int>(
                KeyView{.group = "app.theme", .key = "accent_color"}, 0x2196F3)
        };
    }

    void set_theme(const ThemeConfig& config) {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        store.set(KeyView{.group = "app.theme", .key = "name"},
                  config.name, Layer::User);
        store.set(KeyView{.group = "app.theme", .key = "dark_mode"},
                  config.dark_mode, Layer::User);
        store.set(KeyView{.group = "app.theme", .key = "accent_color"},
                  config.accent_color, Layer::User);

        store.sync(SyncMethod::Async);
    }

    // ========== 网络配置 ==========

    struct NetworkConfig {
        std::string server_url = "https://api.example.com";
        int timeout_ms = 5000;
        int max_retries = 3;
        bool enable_proxy = false;
        std::string proxy_host;
        int proxy_port = 8080;
    };

    NetworkConfig get_network() const {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        return NetworkConfig{
            .server_url = store.query<std::string>(
                KeyView{.group = "network", .key = "server_url"},
                "https://api.example.com"),
            .timeout_ms = clamp_range(
                store.query<int>(KeyView{.group = "network", .key = "timeout_ms"}, 5000),
                1000, 30000),
            .max_retries = clamp_range(
                store.query<int>(KeyView{.group = "network", .key = "max_retries"}, 3),
                0, 10),
            .enable_proxy = store.query<bool>(
                KeyView{.group = "network", .key = "enable_proxy"}, false),
            .proxy_host = store.query<std::string>(
                KeyView{.group = "network", .key = "proxy_host"}, ""),
            .proxy_port = store.query<int>(
                KeyView{.group = "network", .key = "proxy_port"}, 8080)
        };
    }

    // ========== 编辑器配置 ==========

    struct EditorConfig {
        std::string font_family = "Monospace";
        int font_size = 12;
        bool line_numbers = true;
        bool word_wrap = false;
        int tab_size = 4;
    };

    EditorConfig get_editor() const {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        return EditorConfig{
            .font_family = store.query<std::string>(
                KeyView{.group = "editor", .key = "font_family"}, "Monospace"),
            .font_size = clamp_range(
                store.query<int>(KeyView{.group = "editor", .key = "font_size"}, 12),
                8, 72),
            .line_numbers = store.query<bool>(
                KeyView{.group = "editor", .key = "line_numbers"}, true),
            .word_wrap = store.query<bool>(
                KeyView{.group = "editor", .key = "word_wrap"}, false),
            .tab_size = clamp_range(
                store.query<int>(KeyView{.group = "editor", .key = "tab_size"}, 4),
                1, 8)
        };
    }

    // ========== 初始化 ==========

    void initialize() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        // 注册所有配置键
        register_theme_keys();
        register_network_keys();
        register_editor_keys();

        store.sync(SyncMethod::Sync);
    }

private:
    AppConfigManager() = default;
    ~AppConfigManager() = default;

    // 禁止拷贝和移动
    AppConfigManager(const AppConfigManager&) = delete;
    AppConfigManager& operator=(const AppConfigManager&) = delete;

    static int clamp_range(int value, int min_val, int max_val) {
        if (value < min_val) return min_val;
        if (value > max_val) return max_val;
        return value;
    }

    void register_theme_keys() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        store.register_key(
            Key{.full_key = "app.theme.name",
                .full_description = "Application theme name"},
            std::string("default"), Layer::User);

        store.register_key(
            Key{.full_key = "app.theme.dark_mode",
                .full_description = "Enable dark mode"},
            false, Layer::User);

        store.register_key(
            Key{.full_key = "app.theme.accent_color",
                .full_description = "Theme accent color (ARGB)"},
            0x2196F3, Layer::User);
    }

    void register_network_keys() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        store.register_key(
            Key{.full_key = "network.server_url",
                .full_description = "API server URL"},
            std::string("https://api.example.com"), Layer::User);

        store.register_key(
            Key{.full_key = "network.timeout_ms",
                .full_description = "Network request timeout in milliseconds"},
            5000, Layer::User);

        store.register_key(
            Key{.full_key = "network.max_retries",
                .full_description = "Maximum number of retry attempts"},
            3, Layer::User);
    }

    void register_editor_keys() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        store.register_key(
            Key{.full_key = "editor.font_family",
                .full_description = "Editor font family"},
            std::string("Monospace"), Layer::User);

        store.register_key(
            Key{.full_key = "editor.font_size",
                .full_description = "Editor font size in points"},
            12, Layer::User);

        store.register_key(
            Key{.full_key = "editor.tab_size",
                .full_description = "Tab size in spaces"},
            4, Layer::User);
    }
};

// 使用示例
void use_app_config() {
    auto& config = AppConfigManager::instance();

    // 获取配置
    auto theme = config.get_theme();
    auto network = config.get_network();
    auto editor = config.get_editor();

    // 修改配置
    ThemeConfig new_theme;
    new_theme.name = "dark";
    new_theme.dark_mode = true;
    config.set_theme(new_theme);
}

6.2 模式二:热重载支持

实现配置文件的监听和热重载功能:

/**
 * @brief 配置热重载管理器
 *
 * 监听配置文件变化,自动重新加载配置并通知观察者。
 */
class ConfigHotReload {
public:
    using ConfigChangedCallback = std::function<void()>;

    static ConfigHotReload& instance() {
        static ConfigHotReload instance;
        return instance;
    }

    void start_watching() {
        if (watcher_) return;  // 已启动

        // 使用 QFileSystemWatcher 监听配置文件
        watcher_ = std::make_unique<QFileSystemWatcher>();

        // 添加要监听的配置文件路径
        auto paths = get_config_paths();
        for (const auto& path : paths) {
            if (QFile::exists(path)) {
                watcher_->addPath(path);
            }
        }

        // 连接文件变化信号
        QObject::connect(watcher_.get(), &QFileSystemWatcher::fileChanged,
                         this, &ConfigHotReload::on_file_changed);

        // 设置防抖定时器
        debounce_timer_ = std::make_unique<QTimer>();
        debounce_timer_->setSingleShot(true);
        debounce_timer_->setInterval(500);  // 500ms 防抖

        QObject::connect(debounce_timer_.get(), &QTimer::timeout,
                         this, &ConfigHotReload::reload_config);
    }

    void stop_watching() {
        watcher_.reset();
        debounce_timer_.reset();
    }

    void add_observer(ConfigChangedCallback callback) {
        observers_.push_back(std::move(callback));
    }

private:
    ConfigHotReload() = default;
    ~ConfigHotReload() = default;

    QStringList get_config_paths() {
        using namespace cf::config;
        // 获取配置文件路径
        // 这里简化处理,实际应该从 PathProvider 获取
        return {
            QString::fromStdString(get_user_config_path()),
            QString::fromStdString(get_app_config_path())
        };
    }

    std::string get_user_config_path() {
        // 实现获取用户配置路径
        return "~/.config/cfdesktop/user.ini";
    }

    std::string get_app_config_path() {
        // 实现获取应用配置路径
        return "./config/app.ini";
    }

    void on_file_changed(const QString& path) {
        // 文件变化时,启动防抖定时器
        qDebug() << "Config file changed:" << path;
        debounce_timer_->start();
    }

    void reload_config() {
        using namespace cf::config;

        qDebug() << "Reloading configuration...";

        // 重新加载配置
        ConfigStore::instance().reload();

        // 通知所有观察者
        for (auto& observer : observers_) {
            observer();
        }

        qDebug() << "Configuration reloaded";
    }

    std::unique_ptr<QFileSystemWatcher> watcher_;
    std::unique_ptr<QTimer> debounce_timer_;
    std::vector<ConfigChangedCallback> observers_;
};

// 使用示例
class ThemeManager {
public:
    ThemeManager() {
        // 监听配置变化
        ConfigHotReload::instance().add_observer([this]() {
            on_config_reloaded();
        });
    }

    void apply_theme() {
        auto& config = AppConfigManager::instance();
        auto theme = config.get_theme();
        // 应用主题...
        qDebug() << "Theme applied:" << QString::fromStdString(theme.name);
    }

private:
    void on_config_reloaded() {
        qDebug() << "Config reloaded, reapplying theme...";
        apply_theme();
    }
};

// 在应用启动时启用热重载
void initialize_application() {
    // 初始化配置
    AppConfigManager::instance().initialize();

    // 启用热重载 (开发环境)
#ifdef QT_DEBUG
    ConfigHotReload::instance().start_watching();
#endif
}

6.3 模式三:配置迁移

处理配置版本升级时的数据迁移:

/**
 * @brief 配置迁移管理器
 *
 * 处理不同版本配置之间的数据迁移。
 */
class ConfigMigration {
public:
    static ConfigMigration& instance() {
        static ConfigMigration instance;
        return instance;
    }

    /**
     * 执行配置迁移
     * @param from_version 迁移前的版本号
     * @param to_version 迁移后的版本号
     */
    bool migrate(int from_version, int to_version) {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        for (int v = from_version; v < to_version; ++v) {
            if (!migrate_to(v + 1)) {
                return false;
            }
        }

        // 更新版本号
        store.set(KeyView{.group = "app", .key = "config_version"},
                  to_version, Layer::User);
        store.sync(SyncMethod::Sync);

        return true;
    }

    /**
     * 检查是否需要迁移
     */
    bool needs_migration() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        int current_version = store.query<int>(
            KeyView{.group = "app", .key = "config_version"}, 1);
        int target_version = get_target_version();

        return current_version < target_version;
    }

    int get_current_version() const {
        using namespace cf::config;
        return ConfigStore::instance().query<int>(
            KeyView{.group = "app", .key = "config_version"}, 1);
    }

private:
    ConfigMigration() = default;

    static constexpr int get_target_version() { return 2; }

    bool migrate_to(int version) {
        switch (version) {
            case 2:
                return migrate_v1_to_v2();
            // 添加未来的版本迁移
            // case 3:
            //     return migrate_v2_to_v3();
            default:
                return false;
        }
    }

    /**
     * v1 -> v2 迁移
     *
     * 变更:
     * - app.theme.* 重命名为 ui.theme.*
     * - editor.font.family 重命名为 editor.font_family
     */
    bool migrate_v1_to_v2() {
        using namespace cf::config;
        auto& store = ConfigStore::instance();

        qDebug() << "Migrating config from v1 to v2...";

        // 手动通知策略,避免在迁移过程中触发 Watcher
        NotifyPolicy manual = NotifyPolicy::Manual;

        // 迁移 app.theme.* -> ui.theme.*
        migrate_key(store, "app.theme.name", "ui.theme.name", manual);
        migrate_key(store, "app.theme.dark_mode", "ui.theme.dark_mode", manual);
        migrate_key(store, "app.theme.accent_color", "ui.theme.accent_color", manual);

        // 迁移 editor.font.family -> editor.font_family
        migrate_key(store, "editor.font.family", "editor.font_family", manual);

        // 删除旧键
        store.unregister_key(Key{.full_key = "app.theme.name", .full_description = ""},
                            Layer::User, manual);
        store.unregister_key(Key{.full_key = "app.theme.dark_mode", .full_description = ""},
                            Layer::User, manual);
        store.unregister_key(Key{.full_key = "app.theme.accent_color", .full_description = ""},
                            Layer::User, manual);
        store.unregister_key(Key{.full_key = "editor.font.family", .full_description = ""},
                            Layer::User, manual);

        // 触发通知
        store.notify();

        qDebug() << "Migration v1 -> v2 completed";
        return true;
    }

    void migrate_key(cf::config::ConfigStore& store,
                     const std::string& old_key,
                     const std::string& new_key,
                     cf::config::NotifyPolicy policy) {
        // 读取旧值
        auto old_value = get_any_value(store, old_key);
        if (!old_value.has_value()) {
            return;  // 旧键不存在,跳过
        }

        // 写入新键
        set_any_value(store, new_key, *old_value, Layer::User, policy);
    }

    std::optional<std::any> get_any_value(cf::config::ConfigStore& store,
                                          const std::string& key) {
        // 简化实现,实际需要根据类型获取
        // 这里假设所有值都是字符串
        KeyView kv{.group = key.substr(0, key.rfind('.')),
                   .key = key.substr(key.rfind('.') + 1)};
        auto value = store.query<std::string>(kv);
        if (value) {
            return *value;
        }
        return std::nullopt;
    }

    void set_any_value(cf::config::ConfigStore& store,
                       const std::string& key,
                       const std::any& value,
                       Layer layer,
                       NotifyPolicy policy) {
        // 简化实现
        if (value.type() == typeid(std::string)) {
            KeyView kv{.group = key.substr(0, key.rfind('.')),
                       .key = key.substr(key.rfind('.') + 1)};
            store.set(kv, std::any_cast<std::string>(value), layer, policy);
        }
    }
};

// 应用启动时执行迁移
void check_and_migrate_config() {
    auto& migration = ConfigMigration::instance();

    if (migration.needs_migration()) {
        int current = migration.get_current_version();
        int target = 2;  // ConfigMigration::get_target_version()

        qDebug() << "Migrating config from v" << current << " to v" << target;

        if (migration.migrate(current, target)) {
            qDebug() << "Config migration successful";
        } else {
            qWarning() << "Config migration failed";
        }
    }
}

6.4 模式四:多实例隔离(使用自定义路径提供者)

通过自定义路径提供者实现多个 ConfigStore 实例的隔离:

/**
 * @brief 自定义路径提供者
 *
 * 为测试或特殊场景提供自定义配置文件路径。
 */
class CustomPathProvider : public IConfigStorePathProvider {
public:
    /**
     * @brief 创建测试用的路径提供者
     * @param base_path 基础路径
     * @param instance_name 实例名称,用于区分不同实例
     */
    CustomPathProvider(const QString& base_path,
                      const QString& instance_name)
        : base_path_(base_path), instance_name_(instance_name) {

        // 确保目录存在
        QDir().mkpath(base_path);
    }

    QString system_path() const override {
        return base_path_ + "/" + instance_name_ + "_system.ini";
    }

    QString user_dir() const override {
        return base_path_;
    }

    QString user_filename() const override {
        return instance_name_ + "_user.ini";
    }

    QString app_dir() const override {
        return base_path_;
    }

    QString app_filename() const override {
        return instance_name_ + "_app.ini";
    }

    bool is_layer_enabled(int layer_index) const override {
        // 启用所有层
        (void)layer_index;
        return true;
    }

private:
    QString base_path_;
    QString instance_name_;
};

/**
 * @brief 隔离的配置存储包装器
 *
 * 使用不同的配置文件路径创建隔离的配置存储实例。
 */
class IsolatedConfigStore {
public:
    explicit IsolatedConfigStore(const QString& instance_name,
                                 const QString& base_path = "/tmp/test_config")
        : instance_name_(instance_name), base_path_(base_path) {

        // 创建自定义路径提供者
        auto path_provider = std::make_shared<CustomPathProvider>(base_path, instance_name);

        // 初始化 ConfigStore
        cf::config::ConfigStore::instance().initialize(path_provider);
    }

    ~IsolatedConfigStore() {
        // 可选:清理测试文件
        // cleanup();
    }

    void cleanup() {
        QFile::remove(base_path_ + "/" + instance_name_ + "_system.ini");
        QFile::remove(base_path_ + "/" + instance_name_ + "_user.ini");
        QFile::remove(base_path_ + "/" + instance_name_ + "_app.ini");
    }

    cf::config::ConfigStore& store() {
        return cf::config::ConfigStore::instance();
    }

private:
    QString instance_name_;
    QString base_path_;
};

// ========== 使用示例 ==========

// 示例 1:单元测试中的隔离配置
class ConfigTest : public ::testing::Test {
protected:
    void SetUp() override {
        // 每个测试使用独立的配置文件
        test_store_ = std::make_unique<IsolatedConfigStore>(
            QString("test_") + QString::number(std::rand()),  // 随机实例名
            "/tmp/config_tests"
        );
    }

    void TearDown() override {
        test_store_->cleanup();
    }

    cf::config::ConfigStore& config() {
        return test_store_->store();
    }

private:
    std::unique_ptr<IsolatedConfigStore> test_store_;
};

TEST_F(ConfigTest, SetValueAndQuery) {
    auto& store = config();

    store.set(cf::config::KeyView{.group = "test", .key = "value"},
              42, cf::config::Layer::App);

    auto result = store.query<int>(cf::config::KeyView{.group = "test", .key = "value"});
    ASSERT_TRUE(result.has_value());
    EXPECT_EQ(*result, 42);
}

// 示例 2:多用户配置隔离
class MultiUserConfigManager {
public:
    struct UserConfig {
        std::string username;
        std::string theme;
        int font_size;
    };

    void set_user_config(const std::string& username, const UserConfig& config) {
        auto& store = get_user_store(username);

        store.set(cf::config::KeyView{.group = "user", .key = "theme"},
                  config.theme, cf::config::Layer::User);
        store.set(cf::config::KeyView{.group = "user", .key = "font_size"},
                  config.font_size, cf::config::Layer::User);

        store.sync(cf::config::ConfigStore::SyncMethod::Sync);
    }

    UserConfig get_user_config(const std::string& username) {
        auto& store = get_user_store(username);

        return UserConfig{
            .username = username,
            .theme = store.query<std::string>(
                cf::config::KeyView{.group = "user", .key = "theme"}, "default"),
            .font_size = store.query<int>(
                cf::config::KeyView{.group = "user", .key = "font_size"}, 12)
        };
    }

private:
    cf::config::ConfigStore& get_user_store(const std::string& username) {
        // 每个用户使用独立的配置文件
        auto it = user_stores_.find(username);
        if (it == user_stores_.end()) {
            auto store = std::make_unique<IsolatedConfigStore>(
                QString::fromStdString(username),
                "/tmp/multi_user_config"
            );
            it = user_stores_.emplace(username, std::move(store)).first;
        }
        return it->second->store();
    }

    std::unordered_map<std::string, std::unique_ptr<IsolatedConfigStore>> user_stores_;
};

附录:快速参考

A.1 层级优先级速查

层级 优先级 持久化 用途
Temp 3 (最高) 临时数据、测试
App 2 运行时状态
User 1 用户偏好
System 0 (最低) 系统默认

A.2 常用代码片段

// 设置用户偏好
ConfigStore::instance().set(KeyView{.group = "user.theme", .key = "name"},
                            "dark", Layer::User);

// 读取带默认值
auto theme = ConfigStore::instance().query<std::string>(
    KeyView{.group = "user.theme", .key = "name"}, "default");

// 批量修改
store.set(..., Layer::User, NotifyPolicy::Manual);
store.set(..., Layer::User, NotifyPolicy::Manual);
store.notify();
store.sync(SyncMethod::Async);

// 监听变化
auto handle = store.watch("user.theme.*", [](auto... args) {
    // 处理变化
});
store.unwatch(handle);

A.3 错误处理检查清单

  • [ ] 查询时是否提供合理的默认值?
  • [ ] 类型转换是否考虑了失败情况?
  • [ ] 键名是否进行了验证?
  • [ ] Watcher 回调是否避免再次调用 ConfigStore?
  • [ ] 是否使用了异步持久化避免阻塞?
  • [ ] 多线程场景是否考虑了锁的顺序?

文档版本: v1.0 最后更新: 2026-03-17 维护者: CFDesktop 团队