Skip to content

ConfigStore 最佳实践

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

一、键命名规范

键名格式

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

命名规则

规则说明正确示例错误示例
小写字母所有层级使用小写app.theme.nameApp.Theme.Name
点分隔. 作为层级分隔符ui.window.widthui/window/width
语义清晰使用描述性名称,避免缩写max_file_sizemfs
下划线连接多词属性用 _ 连接dark_modedarkMode
避免数字除非是版本或编号schema_v2style1
全英文键名使用英文background_color背景颜色

命名空间预留

text
app.*          # 应用程序级配置
ui.*           # 用户界面配置
editor.*       # 编辑器配置
network.*      # 网络配置
database.*     # 数据库配置
logger.*       # 日志配置

在代码中定义常量以避免拼写错误:

cpp
namespace ConfigKeys {
    constexpr std::string_view APP_PREFIX = "app";
    inline constexpr KeyView APP_THEME_NAME{.group = "app.theme", .key = "name"};
    inline constexpr KeyView UI_WINDOW_WIDTH{.group = "ui.window", .key = "width"};
}

二、层级使用策略

层级优先级

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

各层使用规则

层级持久化谁可修改存储内容
System安装程序/管理员默认配置值、系统级限制(最大文件大小)、硬件相关默认设置、部署环境配置
User最终用户主题/外观设置、语言/区域设置、用户习惯(窗口大小/位置)、功能开关
App应用程序窗口几何状态、最近打开文件列表、会话恢复数据、应用内部状态
Temp应用程序/测试代码单元测试临时配置、功能预览模式、会话令牌、调试标志

层级选择决策表

场景推荐层级
应用默认值 / 系统限制System
用户偏好 / 功能开关User
窗口状态 / 会话数据App
测试数据 / 会话令牌Temp

各层代码示例

cpp
// System 层:系统默认(安装时或首次运行时设置)
store.register_key(Key{.full_key = "file.max_size_mb",
                       .full_description = "Max file size in MB"}, 100, Layer::System);

// User 层:用户偏好
store.set(KeyView{.group = "user.theme", .key = "dark_mode"}, true, Layer::User);
store.set(KeyView{.group = "user.interface", .key = "language"}, "zh_CN", Layer::User);
store.sync(SyncMethod::Async);

// App 层:运行时状态
store.set(KeyView{.group = "app.window", .key = "width"}, geometry.width(), Layer::App);
store.set(KeyView{.group = "app.window", .key = "maximized"}, window->isMaximized(), Layer::App);

// Temp 层:临时数据(不持久化)
store.set(KeyView{.group = "debug", .key = "enabled"}, true, Layer::Temp);

三、性能优化

缓存利用

做法推荐/不推荐说明
优先级查询(省略 Layer 参数)推荐自动返回最高优先级值
手动遍历各层查询不推荐冗余且低效
批量读取配置到结构体推荐减少 API 调用次数
在高频函数中重复 query()不推荐缓存值 + 监听变化更优

批量操作

cpp
// 使用 Manual 策略批量修改,一次性触发 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);
store.notify();
store.sync(SyncMethod::Async);

异步持久化

频繁写入时使用 SyncMethod::Async 避免阻塞主线程;应用退出时使用 SyncMethod::Sync 确保数据落盘。

cpp
// 运行时:延迟异步保存
ConfigStore::instance().sync(SyncMethod::Async);

// 退出时:立即同步保存
ConfigStore::instance().sync(SyncMethod::Sync);

四、线程安全

规则速查

规则说明
读并发安全query() 内部使用 shared_lock,多线程可并发读取
禁止在 Watcher 回调中调用 set()可能导致死锁;改用原子标志位延迟处理
持有外部锁时避免调用 ConfigStore先释放外部锁,再调用 ConfigStore
Watcher 回调应轻量提取信息后入队,由主线程或专用线程处理

典型错误与修正

cpp
// 危险:在 Watcher 回调中再次写入
auto handle = store.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 = store.watch("app.theme", [&theme_changed](const Key&, auto, auto, Layer) {
    theme_changed.store(true);
});

五、错误处理

策略说明
提供默认值query<int>(kv, default_value) 在键不存在时返回默认值
范围校验对读取的值做 min/max 裁剪,防止配置文件被手动篡改导致异常
链式降级先尝试主键,再尝试备用键,最后用硬编码默认值
cpp
// 带范围校验的读取
int timeout = store.query<int>(KeyView{.group = "network", .key = "timeout"}, 1000);
timeout = std::clamp(timeout, 100, 60000);

六、实践模式速查

模式用途关键思路
配置管理器封装类型安全的配置访问单例 + 结构体封装 get/set,集中管理键名和默认值
热重载开发环境监听配置文件变化QFileSystemWatcher + 防抖定时器 + ConfigStore::reload()
配置迁移版本升级时数据迁移递增版本号 + migrate_key() 逐键迁移 + NotifyPolicy::Manual
多实例隔离测试或多用户场景自定义 IConfigStorePathProvider,为每个实例提供独立文件路径

附录:快速参考

层级优先级速查

层级优先级持久化用途
Temp3 (最高)临时数据、测试
App2运行时状态
User1用户偏好
System0 (最低)系统默认

常用代码片段

cpp
// 设置用户偏好
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.notify();
store.sync(SyncMethod::Async);

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

检查清单

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

维护者: CFDesktop 团队

Built with VitePress