C++轻量级配置管理器升级版

通过重构此前的INI轻量级配置管理器,新的配置库的核心设计目标是提供一种统一 的接口来操作多种格式(INI, JSON, XML)的配置文件,同时保证类型安全线程安全 。它通过抽象基类、继承和多态机制,将不同格式的解析细节隐藏起来,为上层应用提供了简洁一致的getset接口,同时支持更简洁的 [] 运算符,使操作更加简单明了。

🧱 一、整体架构概览

整个库的架构清晰,主要由以下几个核心类构成,它们之间的协作关系如下图所示:
聚合 被抛出 包含 1 * 递归包含 (树形结构) 1 * ConfigException +ConfigException(String& msg, int line) <<static>> ConfigErrorHandler +logError(message, throwEx) ConfigNode -m_value: String -m_config: map<String, ConfigNode> ... +to() : T +operator[](key) +add(key, value) +keys() ConfigParser #m_filePath: String #m_config: map<String, ConfigNode> ... +load(filePath) +toString() +get(section, key, defaultValue) : T +set(section, key, value) +sections() IniParser +load(filePath) +toString() -parseLine(line, currentSection, lineNumber) JsonParser +load(filePath) +toString() XmlParser +load(filePath) +toString() ConfigManager -m_mutex: mutex -m_configParser: shared_ptr<ConfigParser> ... +get(section, key, defaultValue) : T +set(section, key, value) +save(filePath) +operator[](section)

  • ConfigException & ConfigErrorHandler:异常处理层
  • ConfigNode:配置数据结构的核心容器
  • ConfigParser:抽象解析器基类,定义通用接口
  • IniParser / JsonParser / XmlParser:具体格式解析器
  • ConfigManager:对外统一接口,封装线程安全与格式识别

⚡ 二、核心类详解

1️⃣ ConfigException & ConfigErrorHandler ------ 错误处理机制

cpp 复制代码
class ConfigException : public std::runtime_error
{
public:
	ConfigException(const String &msg, int line = -1)
		: std::runtime_error(line < 0 ? msg : msg + " at line " + String(line)) {}
};
  • 继承自 std::runtime_error,便于集成标准异常处理机制。
  • 支持带行号的错误信息,便于调试。
cpp 复制代码
class ConfigErrorHandler
{
public:
	static void logError(const String &message, bool throwEx = true)
	{
		std::cerr << "Config Error: " << message << std::endl;
		if(throwEx) throw ConfigException(message);
	}
};
  • 静态工具类,统一错误输出格式。
  • 可选择是否抛出异常,提供灵活性(如日志记录后继续执行)。

2️⃣ ConfigNode ------ 配置数据的核心容器

cpp 复制代码
class ConfigNode
{
public:
	using ConfigConstIterator = std::map<String, ConfigNode>::const_iterator;
	ConfigNode() = default;
	ConfigNode(const String &value);
	ConfigNode(const ConfigNode &node); // 拷贝
	ConfigNode(ConfigNode&& other) noexcept; // 移动

	ConfigNode &operator[](const String &key); // 支持链式访问
	ConfigNode &operator=(const String &value); // 赋值重载
	// ... 其他赋值重载

	size_t erase(const String &key);
	ConfigNode &add(const String &key, const ConfigNode& value);

	template<typename T> T to() const; // 类型转换

	std::vector<String> keys() const;
	ConfigConstIterator find(const String &key) const;
private:
	std::map<String, ConfigNode> m_config; // 子节点
	String m_value; // 当前节点值(叶子)
};
  • 🔍 核心特性:

  • 树形结构: m_config 存储子节点,m_value 存储当前值,支持嵌套配置。

  • 链式访问: config["server"]["port"] = "8080"。

  • 多种赋值方式: 支持 String、double、long、ConfigNode。

  • 移动语义: 提升性能,避免深拷贝开销。

  • 类型转换模板:

cpp 复制代码
template<>
bool ConfigNode::to<bool>() const { ... } // 特化 bool 转换

template<typename T>
inline T ConfigNode::to() const {
    return ::fromString<T>(m_value); // 依赖外部 fromString<T>
}

⚠️ 注意:依赖全局函数 fromString<T>toString,在String.hpp中实现,也可自行实现。

3️⃣ ConfigParser ------ 抽象配置解析器

cpp 复制代码
class ConfigParser
{
public:
	virtual bool load(const String &filePath) = 0;
	virtual String toString() = 0;

	virtual bool save(const String &filePath);
	virtual bool save();

	template<typename T> T get(...) const;
	template<typename T> void set(...);

	bool has(const String &section) const;
	void remove(const String &section, const String &key);

	ConfigNode &operator[](const String &section);

protected:
	String m_filePath;
	std::map<String, ConfigNode> m_config; // 所有节
};

📌 关键方法:

  • get<T>() 模板:
cpp 复制代码
template<typename T>
inline T ConfigParser::get(const String &section, const String &key, const T& defaultValue) const
{
	auto section_it = m_config.find(section);
	if(section_it != m_config.end()) {
		auto key_it = section_it->second.find(key);
		if(key_it != section_it->second.end()) {
			try {
				return ::fromString<T>(key_it->second.toString());
			} catch(...) {}
		}
	}
	return defaultValue; // 容错设计
}
  • 支持默认值,避免程序崩溃。

  • 内部 ·try-catch·,转换失败返回默认值,非异常驱动设计。

  • set<T>() 模板:

cpp 复制代码
template<typename T>
inline void ConfigParser::set(const String &section, const String &key, const T& value)
{
	if(m_config.find(section) == m_config.end()) {
		m_config.insert(std::make_pair(section, ConfigNode()));
	}
	m_config[section][key] = ::toString(value); // 依赖 toString
}
  • 自动创建节(section),无需预先声明。
  • 依赖 ::toString(value),支持自定义类型。

4️⃣ IniParser ------ INI 格式具体实现

cpp 复制代码
class IniParser : public ConfigParser
{
public:
	bool load(const String &filePath) override;
	String toString() override;
private:
	void parseLine(const String &line, const String &currentSection, int lineNumber);
};

📄 INI 文件格式示例:

ini 复制代码
[Database]
host = localhost
port = 5432
ssl = true

[Server]
port = 8080
debug = false

🔍 load() 解析流程:

  • 逐行读取
  • 跳过空行/注释(# 或 ;)
  • Section\] → 设置 currentSection

cpp 复制代码
void IniParser::parseLine(const String &line, const String &currentSection, int lineNumber)
{
	size_t eqPos = line.find('=');
	if(eqPos != String::npos) {
		String key = trim(line.substr(0, eqPos));
		String value = trim(line.substr(eqPos + 1));
		if(!currentSection.empty()) {
			m_config[currentSection][key] = value;
		} else {
			throw ConfigException("No section for key '" + key + "'", lineNumber);
		}
	} else {
		throw ConfigException("Invalid line", lineNumber);
	}
}

📝 toString() 生成格式化输出:

cpp 复制代码
String IniParser::toString()
{
	String result;
	for(const auto& section : sections()) {
		result += "[" + section + "]\n";
		for(const auto& key : keys(section)) {
			result += key + " = " + m_config[section][key].to<String>() + "\n";
		}
		result += "\n";
	}
	return result;
}

✅ 支持自动格式化、节间空行,提高可读性。

5️⃣ JsonParser / XmlParser (待实现)

实现文件解析接口 load 和文件格式化输出接口 toString

cpp 复制代码
bool load(const String &filePath) { ... }
String toString() { ... }

🚀 扩展建议:可集成 nlohmann/json 或 pugixml 库实现完整功能。

6️⃣ ConfigManager ------ 统一入口 & 线程安全网关

cpp 复制代码
class ConfigManager
{
public:
	ConfigManager(const String &filePath);
	bool load(const String &filePath);

	template<typename T> bool set(...);
	template<typename T> T get(...) const;

	bool save();
	std::vector<String> sections();
	ConfigNode &operator[](const String &section);

private:
	mutable std::mutex m_mutex;
	std::shared_ptr<ConfigParser> m_configParser;
};

🔄 自动格式识别:

cpp 复制代码
bool ConfigManager::load(const String &filePath)
{
	String file_type = filePath.subAfter('.').lower();
	if(file_type == "json") m_configParser.reset(new JsonParser());
	else if(file_type == "xml") m_configParser.reset(new XmlParser());
	else m_configParser.reset(new IniParser()); // 默认INI

	return m_configParser->load(filePath);
}

📎链式访问

cpp 复制代码
ConfigNode &operator[](const String &section);
  • 通过实现ConfigManager、ConfigParser、ConfigNode[] 的运算符实现了快捷方便的链式访问

🧪 三、使用示例

cpp 复制代码
#include "src/ConfigManager.hpp"

int main()
{
	ConfigManager config;

	// 1. 加载配置文件
	if(!config.load("app.conf"))
	{
		std::cerr << "Failed to load config file." << std::endl;
		// 可以选择创建一个默认配置
		config.set("Database", "host", "localhost");
		config.set("Database", "port", 5432);
		config.set("Database", "username", "user");
		config.set("Database", "password", "pass");
		config.set("UI", "theme", "dark");
		config.set("UI", "language", "en");
		config.set("UI", "auto_save", true);
		config.save("app.conf"); // 保存默认配置
		std::cout << "Created default config file 'app.conf'." << std::endl;
	}

	// 2. 读取配置值
	String dbHost = config.get<String>("Database", "host", "default_host");
	int dbPort = config.get<int>("Database", "port", 3306); // 默认端口3306
	String theme = config.get<String>("UI", "theme", "light");
	bool autoSave = config.get<bool>("UI", "auto_save", false);

	std::cout << "Database Host: " << dbHost << std::endl;
	std::cout << "Database Port: " << dbPort << std::endl;
	std::cout << "UI Theme: " << theme << std::endl;
	std::cout << "Auto Save: " << (autoSave ? "Enabled" : "Disabled") << std::endl;

	// 3. 修改配置值
	config.set("Database", "host", "newhost.example.com");
	config.set("UI", "language", "zh");
	// 使用[]方式修改
	config["UI"]["theme"] = "light";

	// 4. 添加新的配置项
	config.set("Logging", "level", "DEBUG");
	config.set("Logging", "file", "app.log");
	// 使用[]方式添加配置项
	config["System"]["version"] = "1.0.0";

	// 5. 保存修改后的配置
	if(!config.save())
	{
		std::cerr << "Failed to save config file." << std::endl;
	}
	else
	{
		std::cout << "Config file saved successfully." << std::endl;
	}

	// 6. 检查存在性
	if(config.has("Logging"))
	{
		std::cout << "Section 'Logging' exists." << std::endl;
	}
	if(config.has("UI", "language"))
	{
		std::cout << "Key 'language' exists in section 'UI'." << std::endl;
	}

	// 7. 获取所有节和键
	std::cout << "\nAll Sections:" << std::endl;
	std::cout << config.toString();

	return 0;
}

运行结果:


项目源码

相关推荐
Swift社区2 小时前
如何解决 Vue2 前端项目为何无法访问本地资源(chunk.js 加载一直 pending/转圈)
开发语言·前端·javascript
大飞pkz2 小时前
【设计模式】题目小练2
开发语言·设计模式·c#·题目小练
啟明起鸣2 小时前
【网络编程】从与 TCP 服务器的对比中探讨出 UDP 协议服务器的并发方案(C 语言)
服务器·c语言·开发语言·网络·tcp/ip·udp
007php0072 小时前
Redis高级面试题解析:深入理解Redis的工作原理与优化策略
java·开发语言·redis·nginx·缓存·面试·职场和发展
九章云极AladdinEdu3 小时前
深度学习优化器进化史:从SGD到AdamW的原理与选择
linux·服务器·开发语言·网络·人工智能·深度学习·gpu算力
axban3 小时前
QT M/V架构开发实战:QStandardItemModel介绍
开发语言·数据库·qt
猿究院-赵晨鹤3 小时前
String、StringBuffer 和 StringBuilder 的区别
java·开发语言
I'm a winner3 小时前
第五章:Python 数据结构:列表、元组与字典(一)
开发语言·数据结构·python
葵野寺3 小时前
【RelayMQ】基于 Java 实现轻量级消息队列(九)
java·开发语言·rabbitmq·java-rabbitmq