开发环境
Qt6 & boost
目标
通过模板的匹配方式,实现key-value的配置读取方式,value支持基本类型及自定义类型。
实现
通过重载类型转换类Transformer实现多种类型的和QString的转换
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDataStream>
#include <boost/lexical_cast.hpp>
namespace Conf {
template<class F, class T>
class Transformer {
public:
T operator()(const F &v) const {
return boost::lexical_cast<T>(v);
}
};
template<>
class Transformer<QString, QString> {
public:
QString operator()(const QString &v) const {
return v;
}
};
template<class T>
class Transformer<QString, T> {
public:
T operator()(const QString &v) const {
if (std::is_same<T, short>::value) {
return v.toShort();
} else if (std::is_same<T, ushort>::value) {
return v.toUShort();
} else if (std::is_same<T, int>::value) {
return v.toInt();
} else if (std::is_same<T, uint>::value) {
return v.toUInt();
} else if (std::is_same<T, long>::value) {
return v.toLong();
} else if (std::is_same<T, ulong>::value) {
return v.toULong();
} else if (std::is_same<T, qlonglong>::value) {
return v.toLongLong();
} else if (std::is_same<T, qulonglong>::value) {
return v.toULongLong();
} else if (std::is_same<T, float>::value) {
return v.toFloat();
} else if (std::is_same<T, double>::value) {
return v.toDouble();
}
throw std::runtime_error("unsupported type" + std::string(typeid(T).name()));
// throw std::invalid_argument("Conversion failed");
}
};
template<class T>
class Transformer<T, QString> {
public:
QString operator()(const T &v) const {
if (std::is_arithmetic_v<T>) {
return QString::number(v);
}
throw std::runtime_error("unsupported type" + std::string(typeid(T).name()));
// throw std::invalid_argument("Conversion failed");
}
};
template<class T>
class Transformer<QString, QList<T>> {
public:
QList<T> operator()(const QString &v) const {
QList<T> result;
QJsonDocument doc = QJsonDocument::fromJson(v.toUtf8());
QJsonArray array = doc.array();
for (auto item : array) {
qDebug() << item;
result.append(item.toVariant().value<T>());
}
return result;
}
};
template<class T>
class Transformer<QList<T>, QString> {
public:
QString operator()(const QList<T> &v) const {
QJsonArray array;
for (auto item : v) {
array.append(QJsonValue::fromVariant(QVariant::fromValue(item)));
}
return QJsonDocument(array).toJson();
}
};
};
配置文件的基础数据类ConfigVarBase,用于保存key和对应的描述(description)。派生类ConfigVar存储实际配置数据
#include <memory>
#include <QSharedPointer>
#include <QString>
#include <QReadWriteLock>
#include <QVariant>
#include <QDebug>
#include "transformer.h"
namespace Conf {
class ConfigVarBase {
public:
using ptr = QSharedPointer<ConfigVarBase>;
explicit ConfigVarBase(const QString &name, const QString &desc = "")
: m_name(name.toLower()), m_description(desc) {}
virtual ~ConfigVarBase() = default;
virtual const QString &name() const { return m_name; }
virtual const QString &description() const { return m_description; }
virtual const QString toString() = 0;
virtual bool fromString(const QString &val) = 0;
virtual QString typeName() const = 0;
protected:
QString m_name;
QString m_description;
};
template<class T, class FromStr = Transformer<QString, T>, class ToStr = Transformer<T, QString>>
class ConfigVar : public ConfigVarBase {
public:
using ptr = QSharedPointer<ConfigVar>;
explicit ConfigVar(const QString &name, const T &default_val, const QString &desc = "")
: ConfigVarBase(name, desc), m_val(default_val) {}
const QString toString() override {
try {
QReadLocker locker(&m_mutex);
return ToStr()(m_val);
} catch (const std::exception &e) {
qWarning() << Q_FUNC_INFO << "exception" << e.what() << "convert to string name=" << m_name;
}
return {};
}
bool fromString(const QString &val) override {
try {
setValue(FromStr()(val));
} catch (std::exception& e) {
qWarning() << Q_FUNC_INFO << "exception" << e.what() << " convert: string to name=" << m_name
<< " - " << val;
}
return false;
}
QString typeName() const override { return QString::fromStdString(typeid(T).name()); }
const T &value() {
QReadLocker locker(&m_mutex);
return m_val;
}
void setValue(const T &v) {
{
QReadLocker locker(&m_mutex);
if (v == m_val) {
return;
}
}
QWriteLocker locker(&m_mutex);
m_val = v;
}
protected:
QReadWriteLock m_mutex;
T m_val;
};
}
以上完成了配置系统的基本数据结构的设计。
将配置数据存储到Config类中,通过Config类对数据进行管理。并且禁止Config类的拷贝、移动等操作。
- lookup函数中find_first_not_of,在Qt中可通过正则表达式的方式进行处理。
- 通过loadFromConfFile加载对应配置文件的数据,该数据应是已经在系统中定义好的数据,忽略非法数据。
namespace Conf {
class Config {
public:
using ptr = QSharedPointer<Config>;
using ConfigVarMap = QMap<QString, ConfigVarBase::ptr>;
Config() = default;
virtual ~Config() = default;
Config(const Config&) = delete; // 禁止拷贝构造
Config(const Config&&) = delete; // 禁止移动构造
Config& operator=(const Config&) = delete; // 禁止赋值操作
Config& operator=(const Config&&) = delete; // 禁止移动操作
template<class T>
typename ConfigVar<T>::ptr lookup(const QString &name, const T &defaultValue, const QString &desc = "") {
QWriteLocker locker(&m_mutex);
if (m_dataMap.contains(name)) {
auto tmp = m_dataMap.value(name).dynamicCast<ConfigVar<T>>();
if (tmp) {
return tmp;
} else {
return nullptr;
}
}
auto stdName = name.toStdString();
if(stdName.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") != std::string::npos) {
qWarning() << "Lookup name invalid " << name;
throw std::invalid_argument(stdName);
}
auto v = QSharedPointer<ConfigVar<T>>::create(name, defaultValue, desc);
m_dataMap.insert(name, v);
return v;
}
template<class T>
typename ConfigVar<T>::ptr lookup(const QString &name) {
QReadLocker locker(&m_mutex);
return m_dataMap.value(name, nullptr).dynamicCast<ConfigVar<T>>();
}
ConfigVarBase::ptr lookupBase(const QString &name);
virtual int loadFromConfFile(const QString &path);
private:
ConfigVarMap m_dataMap;
QReadWriteLock m_mutex;
};
}
使用ConfigMgr管理多个配置文件配置数据,并通过单例的方式在系统中生成唯一实例。
namespace Conf {
class ConfigMgr {
public:
using ConfigMap = QMap<QString, Config::ptr>;
ConfigMgr(const ConfigMgr&) = delete; // 禁止拷贝构造
ConfigMgr(const ConfigMgr&&) = delete; // 禁止移动构造
ConfigMgr& operator=(const ConfigMgr&) = delete; // 禁止赋值操作
ConfigMgr& operator=(const ConfigMgr&&) = delete; // 禁止移动操作
static QSharedPointer<ConfigMgr> instance();
Config::ptr configure(const QString &name);
template<class T>
typename ConfigVar<T>::ptr lookup(const QString &confName, const QString &keyName, const T &defaultValue,
const QString &desc = "") {
return configure(confName)->lookup<T>(keyName, defaultValue, desc);
}
template<class T>
typename ConfigVar<T>::ptr lookup(const QString &confName, const QString &keyName) {
QReadLocker locker(&s_mutex);
if (m_confMap.contains(confName)) {
return m_confMap.value(confName)->lookup<T>(keyName);
}
return nullptr;
}
int loadConfigFromFile(const QString &name, const QString &path);
private:
ConfigMgr() = default;
static QReadWriteLock s_mutex;
static QSharedPointer<ConfigMgr> s_confMgr;
static std::once_flag s_initInstanceFlag;
ConfigMap m_confMap;
};
}
namespace Conf {
QReadWriteLock ConfigMgr::s_mutex{};
QSharedPointer<ConfigMgr> ConfigMgr::s_confMgr;
std::once_flag ConfigMgr::s_initInstanceFlag;
ConfigVarBase::ptr Config::lookupBase(const QString &name)
{
QReadLocker locker(&m_mutex);
return m_dataMap.value(name, nullptr);
}
int Config::loadFromConfFile(const QString &path)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
return 1;
}
auto doc = QJsonDocument::fromJson(file.readAll());
file.close();
if (doc.isEmpty()) {
return 2;
}
QList<QPair<QString, QJsonValue>> m_dataList;
if (doc.isObject()) {
auto obj = doc.object();
for (auto it {obj.begin()}; it != obj.end(); ++it) {
const auto &value = it.value();
m_dataList.append(qMakePair(it.key(), value));
}
}
for (const auto &pair : m_dataList) {
const auto &key = pair.first;
const auto &value = pair.second;
if (key.isEmpty()) {
continue;
}
auto var = lookupBase(key.toLower());
if (var) {
QWriteLocker locker(&m_mutex);
switch (value.type()) {
case QJsonValue::Bool: var->fromString(value.toBool(true) ? "true" : "false"); break;
case QJsonValue::Double: var->fromString(QString::number(value.toDouble())); break;
case QJsonValue::String: var->fromString(value.toString()); break;
case QJsonValue::Object: var->fromString(QJsonDocument(value.toObject()).toJson()); break;
case QJsonValue::Array: var->fromString(QJsonDocument(value.toArray()).toJson()); break;
case QJsonValue::Null:
case QJsonValue::Undefined:
break;
}
}
}
return SUCCESS;
}
QSharedPointer<ConfigMgr> ConfigMgr::instance()
{
std::call_once(s_initInstanceFlag, [] {
QWriteLocker locker(&s_mutex);
if (s_confMgr == nullptr) {
s_confMgr = QSharedPointer<ConfigMgr>(new ConfigMgr);
}
});
return s_confMgr;
}
Config::ptr ConfigMgr::configure(const QString &name)
{
QWriteLocker locker(&s_mutex);
if (!m_confMap.contains(name)) {
m_confMap.insert(name, QSharedPointer<Config>::create());
}
return m_confMap.value(name);
}
int ConfigMgr::loadConfigFromFile(const QString &name, const QString &path)
{
QWriteLocker locker(&s_mutex);
if (m_confMap.contains(name)) {
return m_confMap.value(name)->loadFromConfFile(path);
}
return 1;
}
}
class Obj {
public:
Obj() {};
virtual ~Obj() {}
QString name;
int age = 0;
bool operator==(const Obj &obj) const {
return name == obj.name && age == obj.age;
}
friend QDebug operator<<(QDebug out, const Obj &obj) {
out.nospace() << "name:" << obj.name << ", age:" << obj.age;
return out;
}
};
namespace Conf {
// 通过重载Transformer实现ConfVar对class Obj的支持
template<>
class Transformer<QString, Obj> {
public:
Obj operator()(const QString& v) {
auto doc = QJsonDocument::fromJson(v.toUtf8());
Obj p;
p.name = doc.object()["name"].toString();
p.age = doc.object()["age"].toInt();
return p;
}
};
template<>
class Transformer<Obj, QString> {
public:
QString operator()(const Obj& p) {
QJsonObject obj;
obj["name"] = p.name;
obj["age"] = p.age;
QJsonDocument doc;
doc.setObject(obj);
return doc.toJson();
}
};
}
// 初始化一个名为system的配置文件,对应参数为number, 默认值为0
auto numVar = Conf::ConfigMgr::instance()->lookup<int>("system", "number", 0);
// 初始化一个名为system的配置文件,对应参数为obj, 默认值为Qbj()
auto objVar = Conf::ConfigMgr::instance()->lookup<Obj>("system", "obj", {})->value();
// 加载配置文件
auto resutl = Conf::ConfigMgr::instance()->loadConfigFromFile("system", "../conf/test_configure.json");
标签:std,value,const,name,配置,系统,C++,QString,return
From: https://blog.csdn.net/jayshengC/article/details/142435322