首页 > 编程语言 >C++实现一个配置系统

C++实现一个配置系统

时间:2024-09-26 21:49:41浏览次数:10  
标签:std value const name 配置 系统 C++ QString return

开发环境

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类的拷贝、移动等操作。

  1. lookup函数中find_first_not_of,在Qt中可通过正则表达式的方式进行处理。
  2. 通过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

相关文章

  • 【C++习题】2.双指针_移动零
    文章目录题目链接:题目描述:解法(快排的思想:数组划分区间-数组分两块):C++算法代码:图解题目链接:283.移动零题目描述:解法(快排的思想:数组划分区间-数组分两块):双指针算法,利用数组下标充当指针。我们可以用一个cur指针来扫描整个数组,另一个dest指针用来记......
  • OJ在线评测系统 后端 判题机模块预开发 架构分析 使用工厂模式搭建
    判题机模块预开发(架构师)(工厂模式)判题机模块是为了把代码交个代码沙箱去处理得到结果返回代码沙箱梳理判题模块和代码沙箱的关系判题模块:调用代码沙箱把代码和输入交给代码沙箱去执行代码沙箱:只负责接受代码和输入返回编译的结果不负责判题这两个模块完全解耦......
  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【哈希表】2024E-选修
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入输出示例一输入输出说明示例二输入输出说明解题思路代码pythonjavacpp时空复......
  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【DFS/BFS】2024E-开
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入输出示例一输入输出说明示例二输入输出解题思路代码解法一:BFSpythonjavacpp......
  • C++ day07
    C++笔试题合集:1、什么是虚函数?什么是纯虚函数?    1>在类中定义函数时,在函数名前加上virtual关键字,该函数就是虚函数,虚函数可以保证在父子类中只有一个该函数。    2>当虚函数头=0;时该函数就是纯虚函数,当类中的某些成员函数没有实现的意义,仅仅时该类中拥有......
  • 文件系统:Nodejs `fs` 模块
    node.js中的fs(文件系统)模块是一个用于处理文件系统的强大工具,允许您与服务器上的文件和目录进行交互。它内置于node.js中,因此您无需安装任何额外的东西即可使用它。让我们来探讨一下fs的工作原理及其关键功能。1.什么是fs模块?fs模块提供了一个api,用于以紧密围绕标准......
  • Java毕业设计-基于SSM框架的超市会员(积分)管理系统项目实战(附源码+论文)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 备战软考06——操作系统概述
    操作系统概述文老师软考教育◆操作系统定义:能有效地组织和管理系统中的各种软/硬件资源,合理地组织计算机系统工作流程,控制程序的执行,并且向用户提供一个良好的工作环境和友好的接口。◆操作系统有两个重要的作用:第一,通过资源管理提高计算机系统的效率;第二,改善人机界面向用户......
  • 如何打造Java SpringBoot民宿山庄农家乐系统?2025最新毕业设计攻略
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、小程序、大数据实战项目集⚡⚡文末获取......
  • 如何打造流浪天使乐园管理系统?Java SpringBoot+Vue技术解析
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、小程序、大数据实战项目集⚡⚡文末获取......