• Sylar_网络框架学习——配置系统(二)


    9-15课

    配置模块

    yaml-cpp

    https://github.com/jbeder/yaml-cpp
    在服务器上克隆资源
    git clone https://github.com/jbeder/yaml-cpp
    然后进入文件夹
    cd yaml-cpp
    mkdir build
    cd build
    cmake ..
    make install
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    yaml-cpp安装路径

    ubuntu默认安装在/usr/local/include/
    配置CMakeLists.txt时需要用到

    yaml-cpp使用

    //使用LoadFile加载yaml文件,可以使用cout<<输出
    YAML::Node root = YAML::LoadFile("/home/ubuntu/StudyCpp/sylar/bin/conf/log.yaml");
    
    //yaml中node有不同的结点 分别使用不同方法遍历
    
    // Null 空节点
    // Sequence 序列,类似于一个Vector,对应YAML格式中的数组
    if(node.IsSequence()){
        for(size_t i = 0; i < node.size(); ++i){
            SYLAR_LOG_INFO(SYLAR_GET_ROOT()) << std::string(level * 4, ' ') 
                << i << " - " << node[i].Type() << " - " <<level;
            print_yaml(node[i], level + 1);
        }
    }
    // Map 类似标准库中的Map,对应YAML格式中的对象
    if(node.IsMap()){
        for(auto it = node.begin();
            it != node.end(); ++it){
            SYLAR_LOG_INFO(SYLAR_GET_ROOT()) << std::string(level * 4, ' ') 
                << it->first << " - " << it->second.Type() << " - " <<level;
            print_yaml(it->second, level + 1);
        }
    }
    // Scalar 标量,对应YAML格式中的常量
    //Load从字符串加载转为yaml格式,在string转T的类模板中使用
    YAML::Node root = YAML::Load(std::string);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    调用流程

    读入yaml,调用sylar::Config::LoadFromYaml(root);将yaml结点展平为map格式,键为字符串,值为yaml结点。找到在s_datas中存储过的进行更新,需要进行反序列化fromString,从string中恢复原有类型存入ConfigVar的m_val中。

    静态map存储关键字对应ConfigVarBase基类指针

    static ConfigVarMap s_datas;	//typedef std::map ConfigVarMap;
    
    • 1

    Lookup查找是否存在该关键字,若没有则存储

    //用户创建
    sylar::ConfigVar<float>::ptr g_float_value_config = 
        sylar::Config::Lookup("system.value", (float)10.21f, "system value");
    //Lookup返回一个ConfigVar类型的智能指针
    //Lookup函数内部:
    typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
    s_datas[name] = v;		//存储的是ConfigVar对象,由父类指针指向
    //在创建ConfigVar对象时,m_val存储的是真实值,例如:int a,float b,vector c
    template<class T, class FromStr = LexicalCast<std::string, T>
                    , class ToStr = LexicalCast<T, std::string> >
    class ConfigVar : public ConfigVarBase;	//默认tostring由T转string,fromstring由string转T
    //交由LexicalCast类模板实现
    std::string toString() override;
    //交由LexicalCast类模板实现
    bool fromString(const std::string& val) override;
    //两个类模板都重载了()运算符,使用仿函数的方式调用
    //对于STL容器需要进行偏特化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    类模板

    //给定T类型,从FromStr类转换为ToStr类
    template<T, FromStr, ToStr>
    class ConfigVar;
    
    //转换类模板,从F类型转到T类型
    template<F, T>
    class LexicalCast;
    
    //容器偏特化
    //string转vector
    template<class T>
    class LexicalCast<std::string, std::vector<T> > {
    public:
        std::vector<T> operator() (const std::string& v) {
            YAML::Node node = YAML::Load(v);
            //这里的typename是类型标识符,因为编译时还不能确定vector的类型,先在此声明
            typename std::vector<T> vec;
            std::stringstream ss;
            for(size_t i = 0; i < node.size(); ++i){
                ss.str("");
                ss << node[i];
                vec.push_back(LexicalCast<std::string, T>() (ss.str()));	//需要取出vector中每个元素交由LexicalCast实现
            }
            return vec;
        }
    };
    //vector转string
    template<class T>
    class LexicalCast<std::vector<T>, std::string> {
    public:
        std::string operator() (const std::vector<T>& v) {
            YAML::Node node;
            for(auto& i : v){
                node.push_back(YAML::Load(LexicalCast<T, std::string>() (i)) );
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    STL容器支持

    对不同容器实现模板偏特化,list、set、unordered_set和vector实现相同

    //string转list
    template<class T>
    class LexicalCast<std::string, std::list<T> >;
    //list转string
    template<class T>
    class LexicalCast<std::list<T>, std::string>;
    //string转set
    template<class T>
    class LexicalCast<std::string, std::set<T> >;
    //set转string
    template<class T>
    class LexicalCast<std::set<T>, std::string>;
    //string转unordered_set
    template<class T>
    class LexicalCast<std::string, std::unordered_set<T> >;
    //unordered_set转string
    template<class T>
    class LexicalCast<std::unordered_set<T>, std::string>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    map转换:map

    //偏特化 string --> map
    template<class T>
    class LexicalCast<std::string, std::map<std::string, T> > {
    public:
        std::map<std::string, T> operator() (const std::string& v) {
            YAML::Node node = YAML::Load(v);
            //这里的typename是类型标识符,因为编译时还不能确定map的类型,先在此声明
            typename std::map<std::string, T> vec;
            std::stringstream ss;
            for(auto it = node.begin();
                     it != node.end(); ++it) {
                ss.str("");
                ss << it->second;
                vec.insert(std::make_pair(it->first.Scalar(),
                            LexicalCast<std::string, T>() (ss.str())));
            }
            return vec;
        }
    };
    //偏特化 map --> string
    template<class T>
    class LexicalCast<std::map<std::string, T>, std::string> {
    public:
        std::string operator() (const std::map<std::string, T>& v) {
            YAML::Node node;
            for(auto& it : v){
                node[it.first] = YAML::Load(LexicalCast<T, std::string>() (it.second));
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };
    //string转unordered_map
    template<class T>
    class LexicalCast<std::string, std::unordered_map<std::string, T> >;
    //unordered_map转string
    template<class T>
    class LexicalCast<std::unordered_map<std::string, T>, std::string>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    自定义类型支持

    需要对自定义类型实现模板偏特化

    //Person类
    class Person{
    public:
        std::string m_name;
        int m_age = 0;
        bool m_sex = 0;
        std::string toString() const {
            std::stringstream ss;
            ss  << "[Person name = " <<m_name
                <<" age = " << m_age
                <<" sex = " << m_sex
                <<" ]";
            return ss.str();
        }
        bool operator== (const Person& other) const {
            return this->m_name == other.m_name
                    &&this->m_age == other.m_age
                    &&this->m_sex == other.m_sex;
        }
    };
    //对Person类实现模板偏特化
    namespace sylar{
    //string转Person
    template<>
    class LexicalCast<std::string, Person> {
    public:
        Person operator() (const std::string& v) {
            YAML::Node node = YAML::Load(v);
            //这里的typename是类型标识符,因为编译时还不能确定vector的类型,先在此声明
            Person p;
            p.m_name = node["name"].as<std::string>();
            p.m_age = node["age"].as<int>();
            p.m_sex = node["sex"].as<bool>();
            return p;
        }
    };
    //Person转string
    template<>
    class LexicalCast<Person, std::string> {
    public:
        std::string operator() (const Person& p) {
            YAML::Node node;
            node["name"] = p.m_name;
            node["age"] = p.m_age;
            node["sex"] = p.m_sex;
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };
    }
    //使用
    sylar::ConfigVar<Person>::ptr g_person_config = 
        sylar::Config::Lookup("class.person", Person(), "class person");
    SYLAR_LOG_INFO(SYLAR_GET_ROOT()) << "before:" << g_person_config->getValue().toString() << " - " << g_person_config->toString();
    /*
    before:[Person name =  age = 0 sex = 0 ] - name: ""
    age: 0
    sex: false
    */
    //还可以和STL容器嵌套使用
    sylar::ConfigVar<std::map<std::string, Person> >::ptr g_person_map_config = 
        sylar::Config::Lookup("class.map", std::map<std::string, Person>{}, "class person map");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    配置变更

    配置发生变更后通知持有配置的所有用户 ----> 观察者模式

    用户首先注册回调事件,当配置发生改变时调用回调函数,达到通知的目的

    typedef std::function<void (const T& old_value, const T& new_value)> on_change_cb;	//function包装一个回调函数,需要知道原值和新值
    //变更回调函数组,当注册的事件发生变化时,调用对应的回调函数
    std::map<uint64_t, on_change_cb> m_cbs;
    //实现对回调数组的操作
    void setListener(uint64_t key, on_change_cb cb) {
        m_cbs[key] = cb;
    }
    void delListtener(uint64_t key) {
        m_cbs.erase(key);
    }
    on_change_cb getListener(uint64_t key) {
        auto it = m_cbs.find(key);
        return it == m_cbs.end() ? nullptr : it->second;
    }
    void clearListener() {
        m_cbs.clear();
    }
    //在更改配置的时候调用回调函数
    void setValue(const T& v) {
        //如果改变和当前一样,直接返回
        if(v == m_val){
            return ;
        }
        //遍历回调数组 找到类型相同,但值不同的回调运行
        for(auto& i : m_cbs){
            //这里m_val是原值,v是新值
            i.second(m_val, v);
        }
        m_val = v;
    }
    //用户注册回调事件
    g_person_config->setListener(10, [](const Person& old_val, const Person& new_val){
        SYLAR_LOG_INFO(SYLAR_GET_ROOT()) << "old_val=" << old_val.toString() 
            << "new_val=" << new_val.toString();
    });	//old_val=[Person name =  age = 0 sex = 0 ]new_val=[Person name = sylar age = 35 sex = 1 ]
    //g_person_config是新建配置:
    sylar::ConfigVar<Person>::ptr g_person_config = 
        sylar::Config::Lookup("class.person", Person(), "class person");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    全部实现可见仓库,欢迎点赞。

  • 相关阅读:
    OpenCV C++双目三维重建:双目摄像头实现双目测距
    c++-基本数据类型
    SpringSecurity详解
    【C++】string类 _模拟实现 _源码[复习专用]
    MAML++:HOW TO TRAIN YOUR MAML论文精读
    (仿牛客社区项目)Java开发笔记0:项目目录
    Dos慢速攻击
    B树和B+树的区别
    openEuler安全配置规范基线
    selenium初始学习--打开新标签操作
  • 原文地址:https://blog.csdn.net/jiaoooooo/article/details/127671424