sylar 配置系统

这是个配置系统,有什么可以优化的地方?

cpp 复制代码
class ConfigVarBase {
public:
    typedef std::shared_ptr<ConfigVarBase> ptr;
    ConfigVarBase(const std::string& name, const std::string& description = "")
        :m_name(name)                                                           // name 配置参数名称[0-9a-z_.]
        ,m_description(description)                                             // description 配置参数描述
    {
        std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);// 将名称变成大小写不明感的
    }
    virtual ~ConfigVarBase() {}                                                 // 有具体类型的子类,所以需要变成 虚析构

    const std::string& getName() const          { return m_name; }              // 返回配置参数名称
    const std::string& getDescription() const   { return m_description; }       // 返回配置参数的描述

    virtual std::string toString() = 0;                                         // 转成字符串
    virtual bool fromString(const std::string& value) = 0;                      // 从字符串初始化值
    virtual std::string getTypeName() const = 0;                                // 返回配置参数值的类型名称

protected:
    std::string m_name;                                                         // 配置参数的名称
    std::string m_description;                                                  // 配置参数的描述
};

// 使用模板类来实现不同值类型的子类
template<class T, class FromStr = LexicalCast<std::string, T>, class ToStr = LexicalCast<T, std::string>>    // 值为默认的基础类型的转换函数
class ConfigVar : public ConfigVarBase {
public:
    typedef std::shared_ptr<ConfigVar> ptr;
    typedef std::function<void (const T& old_value, const T& new_value)> on_change_callback;

    ConfigVar(const std::string& name, const T& default_value, const std::string& description)
        :ConfigVarBase(name, description)
        ,m_value(default_value)
    {
    }
    // ~ConfigVar() {}  // 不用

    std::string toString() override     // 原本想把此函数提出去放在公共函数中,但是看到 catch中的输出信息, 它是专有的
    {
        try{
            // return boost::lexical_cast<std::string>(m_value);    // 此方法只对简单Scalar类型有用
            return ToStr()(m_value);
        } catch(const std::exception& e){
            MYLOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception" << e.what() 
                                          << " convert: " << typeid(m_value).name() << " to string";
            // std::cerr << e.what() << '\n';
        }
        return "";
    }

    bool fromString(const std::string& value) override
    {   // "vector1: [11, 22, 33]\nvector2: [101, 202, 303]"
        try
        {
            // m_value = boost::lexical_cast<T>(value);                             // 此方法只对简单Scalar类型有用
            setValue(FromStr()(value));     // m_value = FromStr()(value);          // 仿函数 实现
        } catch(const std::exception& e)
        {
            MYLOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception " << e.what() 
                                          << " convert: string to " << typeid(m_value).name();
            // std::cerr << e.what() << '\n';
        }
        return false;
    }

    const T getValue() const                                { return m_value; }
    void setValue(const T& value)   {
        if(value == m_value)  return;
        for(auto& i : m_callbacks) {
            i.second(m_value, value);
        }
        m_value = value;
    }
    void addListener(uint64_t key, on_change_callback cb)   { m_callbacks[key] = cb; }
    void deleteListener(uint64_t key)                       { m_callbacks.erase(key); }
    void clearListener()                                    { m_callbacks.clear(); }
    on_change_callback getListener(uint64_t key) {
        auto it = m_callbacks.find(key);
        return it == m_callbacks.end() ? nullptr : it->second;
    }

private:
    T m_value;                                              // 存储配置项的当前值,类型为 T
    std::map<uint64_t, on_change_callback> m_callbacks;     // 存储配置项值变化时的回调函数,键为 uint64_t,值为 on_change_callback
};

/// ******************** 配置管理类 ********************
class Config {
public:
    typedef std::map<std::string, ConfigVarBase::ptr> ConfigVarMap;

    // 查找
    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name)
    {
        auto it =  s_datas.find(name);
        if(it == s_datas.end()) return nullptr;                             // 未找到
        return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);         // 找到转换成智能指针
    }

    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "")
    {                                                                       // 诚实的讲,我觉得这个函数叫做 register()更好。 注册默认(default)配置
        auto tmp = Lookup<T>(name);
        if(tmp)  MYLOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name = " << name << " exists";
        if(name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._012345678") != std::string::npos)
        {                                                                   // 发现异常
            MYLOG_ERROR(SYLAR_LOG_ROOT()) <<"Lookup name invalid " << name;
            throw std::invalid_argument(name);
        }
        typename ConfigVar<T>::ptr value(new ConfigVar<T>(name, default_value, description));
        s_datas[name] = value;
        return value;
    }

    // YAML与日志的整合
    // ConfigVarBase::ptr Config::LookupBase(const std::string& name)
    // {
    //     auto it = s_datas.find(name);
    //     return it == s_datas.end() ? nullptr : it->second;
    // }        // 一个方法 只被这个类使用,就写在这个类中

    static void LoadFromYaml(const YAML::Node& root);                       // 使用YAML::Node初始化配置模块
    static ConfigVarBase::ptr LookupBase(const std::string& name);          // 查找配置参数,返回配置参数的基类(name 配置参数名称)

private:
    static ConfigVarMap s_datas;
};
帮我仔细分析该代码。这是个配置系统,有什么可以优化的地方

这个配置系统的设计非常清晰,主要分为两个部分:ConfigVarBaseConfigVar 用于管理配置项,Config 类用于管理所有配置项并提供查找和加载功能。以下是对代码的详细分析以及一些优化建议。

1. ConfigVarBase

ConfigVarBase 是一个基类,定义了配置项的基本属性和接口。它的主要职责是:

  • 存储配置项的名称和描述。
  • 提供虚函数接口,如 toStringfromStringgetTypeName,供子类实现。
优化建议:
  • 名称校验 :在构造函数中,虽然对名称进行了大小写转换,但没有对名称的合法性进行校验。建议在构造函数中加入名称合法性校验,确保名称符合 [0-9a-z_.] 的规则。
  • 虚析构函数:虚析构函数的定义是正确的,但可以在析构函数中加入日志输出,以便在调试时跟踪对象的生命周期。

2. ConfigVar

ConfigVarConfigVarBase 的模板子类,用于存储特定类型的配置项值,并提供了值的变化回调机制。

优化建议:
  • 回调机制m_callbacks 使用 std::map<uint64_t, on_change_callback> 来存储回调函数。可以考虑使用 std::unordered_map 来提高查找效率,尤其是在回调函数数量较多的情况下。
  • 异常处理 :在 toStringfromString 中,异常处理是必要的,但可以考虑将异常信息格式化输出,以便更好地定位问题。
  • 线程安全 :如果配置系统在多线程环境中使用,setValue 和回调函数的调用可能会导致竞态条件。建议使用 std::mutex 来保护 m_valuem_callbacks 的访问。

3. Config

Config 类是一个单例类,用于管理所有的配置项。它提供了查找和注册配置项的功能,并支持从 YAML 文件加载配置。

优化建议:
  • 名称校验 :在 Lookup 函数中,虽然对名称进行了简单的校验,但校验逻辑可以更加严格。例如,可以检查名称是否以数字开头,或者是否包含非法字符。
  • YAML 加载LoadFromYaml 函数的实现没有提供,但建议在加载 YAML 文件时,对文件格式进行校验,并在加载失败时提供详细的错误信息。
  • 线程安全s_datas 是一个静态成员变量,存储了所有的配置项。如果配置系统在多线程环境中使用,s_datas 的访问需要加锁保护。

就是这个类管理配置的读取解析,以及存放每一条配置

4. 其他优化建议

  • 日志输出 :代码中使用了 MYLOG_ERRORMYLOG_INFO 来输出日志,建议在日志输出中加入更多的上下文信息,例如配置项的名称和当前值,以便更好地调试。
  • 性能优化 :如果配置项的数量非常多,可以考虑使用更高效的数据结构来存储配置项,例如 std::unordered_map
  • 代码复用toStringfromString 的实现中有一些重复的代码,可以考虑将这些代码提取到一个辅助函数中,以减少代码重复。

5. 示例优化代码

以下是一些优化后的代码示例:

cpp 复制代码
// ConfigVarBase 构造函数中加入名称校验
ConfigVarBase(const std::string& name, const std::string& description = "")
    : m_name(name), m_description(description)
{
    if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._012345678") != std::string::npos) {
        throw std::invalid_argument("Invalid config name: " + name);
    }
    std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
}

// ConfigVar 中加入线程安全保护
template<class T, class FromStr = LexicalCast<std::string, T>, class ToStr = LexicalCast<T, std::string>>
class ConfigVar : public ConfigVarBase {
public:
    // ... 其他代码不变

    void setValue(const T& value) {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (value == m_value) return;
        for (auto& i : m_callbacks) {
            i.second(m_value, value);
        }
        m_value = value;
    }

private:
    T m_value;
    std::map<uint64_t, on_change_callback> m_callbacks;
    std::mutex m_mutex;
};

// Config 类中加入线程安全保护
class Config {
public:
    // ... 其他代码不变

    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "") {
        std::lock_guard<std::mutex> lock(s_mutex);
        auto tmp = Lookup<T>(name);
        if (tmp) MYLOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name = " << name << " exists";
        if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._012345678") != std::string::npos) {
            MYLOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name;
            throw std::invalid_argument(name);
        }
        typename ConfigVar<T>::ptr value(new ConfigVar<T>(name, default_value, description));
        s_datas[name] = value;
        return value;
    }

private:
    static ConfigVarMap s_datas;
    static std::mutex s_mutex;
};

总结

这个配置系统的设计已经非常完善,但在多线程环境下的安全性、日志输出的详细程度以及代码复用方面还有优化空间。通过加入线程安全保护、严格的名称校验和更详细的日志输出,可以进一步提高系统的稳定性和可维护性。

详细分析一下 Config 类,怎么使用,怎么设计的?

Config 类是配置系统的核心管理类,负责注册、查找和管理所有的配置项。它的设计目标是提供一个统一的接口来操作配置项,并支持从外部数据源(如 YAML 文件)加载配置。

以下是对 Config 类的详细分析,包括它的设计思路、使用方法以及可能的优化点。


设计分析

1. 核心职责

Config 类的主要职责包括:

  • 注册配置项:允许用户注册新的配置项,并指定默认值、名称和描述。
  • 查找配置项:根据名称查找已注册的配置项。
  • 管理配置项:存储所有配置项,并提供统一的访问接口。
  • 加载配置:支持从外部数据源(如 YAML 文件)加载配置值。

2. 数据结构

Config 类的核心数据结构是一个 std::map,用于存储所有配置项:

cpp 复制代码
typedef std::map<std::string, ConfigVarBase::ptr> ConfigVarMap;
static ConfigVarMap s_datas;
  • Key :配置项的名称(std::string)。
  • Value :配置项的智能指针(ConfigVarBase::ptr)。

通过这个映射表,Config 类可以快速查找和管理配置项。


3. 主要方法

(1) Lookup 方法

Lookup 方法用于查找或注册配置项。它有两个重载版本:

  • 查找配置项

    cpp 复制代码
    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name);

    根据名称查找配置项,如果找到则返回对应的 ConfigVar<T> 智能指针,否则返回 nullptr

(1.1) 注册配置/查找配置项: 这个函数名可以是register
cpp 复制代码
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "");

如果配置项不存在,则注册一个新的配置项,并指定默认值和描述;如果配置项已存在,则返回现有的配置项。

(2) LoadFromYaml 方法
cpp 复制代码
static void LoadFromYaml(const YAML::Node& root);

从 YAML 文件加载配置值,并将其应用到对应的配置项中。YAML 文件的结构应与配置项的名称匹配。

(3) LookupBase 方法
cpp 复制代码
static ConfigVarBase::ptr LookupBase(const std::string& name);

根据名称查找配置项,返回基类指针。这个方法主要用于内部实现,或者在不关心具体类型时使用。


4. 设计亮点

  • 模板化的设计:通过模板参数支持任意类型的配置项,提供了高度的灵活性。
  • 统一的接口 :通过 ConfigVarBase 基类,提供了统一的接口来操作配置项(如 toStringfromString)。
  • 懒加载机制:配置项在第一次访问时才会被创建,避免了不必要的初始化开销。
  • 线程安全:通过加锁(如果需要)可以支持多线程环境下的安全访问。

使用方法

1. 注册配置项

通过 Lookup 方法注册一个新的配置项:

cpp 复制代码
auto int_config = Config::Lookup<int>("test.int", 100, "An integer config");
auto str_config = Config::Lookup<std::string>("test.str", "default", "A string config");
auto map_config = Config::Lookup<std::map<std::string, int>>("test.map", {{"x", 1}, {"y", 2}}, "A map config");

2. 查找配置项

通过 Lookup 方法查找已注册的配置项:

cpp 复制代码
auto int_config = Config::Lookup<int>("test.int");
if (int_config) {
    std::cout << "Value: " << int_config->getValue() << std::endl;
} else {
    std::cout << "Config not found!" << std::endl;
}

3. 修改配置项

通过 setValue 方法修改配置项的值:

cpp 复制代码
int_config->setValue(200);

4. 从 YAML 文件加载配置

假设有一个 YAML 文件 config.yaml

yaml 复制代码
test:
  int: 300
  str: "new_value"
  map:
    x: 10
    y: 20

通过 LoadFromYaml 方法加载配置:

cpp 复制代码
YAML::Node root = YAML::LoadFile("config.yaml");
Config::LoadFromYaml(root);

5. 监听配置项变化

通过 addListener 方法注册回调函数,监听配置项的变化:

cpp 复制代码
int_config->addListener(1, [](const int& old_value, const int& new_value) {
    std::cout << "Value changed from " << old_value << " to " << new_value << std::endl;
});

优化建议

1. 线程安全

如果配置系统在多线程环境中使用,需要对 s_datas 的访问加锁:

cpp 复制代码
static std::mutex s_mutex;

template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "") {
    std::lock_guard<std::mutex> lock(s_mutex);
    auto tmp = Lookup<T>(name);
    if (tmp) return tmp;
    auto value = std::make_shared<ConfigVar<T>>(name, default_value, description);
    s_datas[name] = value;
    return value;
}

2. 名称校验

在注册配置项时,可以对名称进行更严格的校验,确保名称符合规范:

cpp 复制代码
if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._012345678") != std::string::npos) {
    throw std::invalid_argument("Invalid config name: " + name);
}

3. 性能优化

如果配置项数量非常多,可以考虑使用 std::unordered_map 代替 std::map,以提高查找性能:

cpp 复制代码
typedef std::unordered_map<std::string, ConfigVarBase::ptr> ConfigVarMap;

4. 支持嵌套结构

如果需要支持嵌套的配置项(如 test.map.x),可以扩展 LoadFromYaml 方法,递归解析 YAML 节点。


总结

Config 类的设计非常清晰,通过模板化和基类的结合,提供了灵活且强大的配置管理功能。它的核心思想是通过名称管理配置项,并支持从外部数据源加载配置。

优点

  • 灵活性:支持任意类型的配置项。
  • 易用性:提供简单的接口来注册、查找和修改配置项。
  • 扩展性 :通过 LexicalCastLoadFromYaml,可以轻松支持新类型和数据源。

改进点

  • 线程安全:在多线程环境中需要加锁。
  • 名称校验:可以增加更严格的名称校验。
  • 性能优化:对于大规模配置项,可以使用更高效的数据结构。

通过合理的设计和优化,Config 类可以成为一个高效、易用的配置管理工具。

"如果配置项不存在,则注册一个新的配置项,并指定默认值和描述;如果配置项已存在,则返回现有的配置项。" 这段描述的啥意思是 如果配置项已存在,则返回现有的配置项。那如果我想中途修改呢,就是即使它有,我也去修改该配置项?

你提到的"如果配置项已存在,则返回现有的配置项"是 Config::Lookup 方法的一种默认行为。它的设计初衷是避免重复注册相同的配置项,确保配置项的唯一性。然而,如果你希望在配置项已存在的情况下仍然能够修改它,可以通过以下几种方式实现:


1. 直接修改配置项的值

即使配置项已存在,你也可以通过 Lookup 方法获取到它的指针,然后调用 setValue 方法来修改它的值。

示例代码

cpp 复制代码
// 注册或查找配置项
auto int_config = Config::Lookup<int>("test.int", 100, "An integer config");

// 修改配置项的值
int_config->setValue(200);

在这种情况下,Lookup 方法的作用是:

  • 如果配置项不存在,则创建一个新的配置项,并设置默认值。
  • 如果配置项已存在,则返回现有的配置项,允许你通过 setValue 修改它的值。

2. 强制覆盖配置项

如果你希望在配置项已存在的情况下,强制覆盖它的默认值和描述,可以扩展 Lookup 方法,增加一个参数来控制是否覆盖。

示例代码

cpp 复制代码
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "", bool force_update = false) {
    auto it = s_datas.find(name);
    if (it != s_datas.end()) {
        auto existing_config = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
        if (existing_config) {
            if (force_update) {
                existing_config->setValue(default_value);
                existing_config->setDescription(description);
            }
            return existing_config;
        } else {
            throw std::invalid_argument("Type mismatch for config: " + name);
        }
    }
    auto new_config = std::make_shared<ConfigVar<T>>(name, default_value, description);
    s_datas[name] = new_config;
    return new_config;
}

使用示例

cpp 复制代码
// 注册或查找配置项
auto int_config = Config::Lookup<int>("test.int", 100, "An integer config");

// 强制覆盖配置项的默认值和描述
int_config = Config::Lookup<int>("test.int", 300, "Updated description", true);

在这个实现中:

  • 如果 force_updatetrue,即使配置项已存在,也会覆盖它的值和描述。
  • 如果 force_updatefalse,则保持现有配置项不变。

3. 删除并重新注册配置项

如果你希望完全替换一个已存在的配置项,可以先删除它,然后重新注册一个新的配置项。

示例代码

cpp 复制代码
template<class T>
static void ReplaceConfig(const std::string& name, const T& default_value, const std::string& description = "") {
    s_datas.erase(name); // 删除现有的配置项
    auto new_config = std::make_shared<ConfigVar<T>>(name, default_value, description);
    s_datas[name] = new_config;
}

使用示例

cpp 复制代码
// 注册配置项
auto int_config = Config::Lookup<int>("test.int", 100, "An integer config");

// 替换配置项
Config::ReplaceConfig<int>("test.int", 300, "Updated description");

4. 动态修改配置项的值

如果你只是希望动态修改配置项的值,而不关心它的默认值或描述,可以直接通过 setValue 方法实现。

示例代码

cpp 复制代码
// 注册或查找配置项
auto int_config = Config::Lookup<int>("test.int", 100, "An integer config");

// 动态修改值
int_config->setValue(200);

总结

默认行为

  • Config::Lookup 的默认行为是:如果配置项已存在,则返回现有的配置项,避免重复注册。
  • 你可以通过 setValue 方法动态修改配置项的值。

扩展行为

  • 如果你希望强制覆盖配置项的默认值和描述,可以扩展 Lookup 方法,增加 force_update 参数。
  • 如果你希望完全替换配置项,可以先删除现有的配置项,然后重新注册一个新的配置项。

选择哪种方式?

  • 动态修改值 :如果只是修改值,使用 setValue 是最简单的方式。
  • 强制覆盖 :如果需要修改默认值和描述,可以使用扩展的 Lookup 方法。
  • 完全替换:如果需要彻底替换配置项,可以先删除再重新注册。

根据你的具体需求,选择最合适的方式即可。

这两个函数的主要作用是从YAML配置文件中加载配置,并将其存储到内存中。下面我们详细分析这两个函数的功能和实现。

1. ListAllMember 函数

cpp 复制代码
static void ListAllMember(const std::string& prefix,
                          const YAML::Node& node,
                          std::list<std::pair<std::string, const YAML::Node> >& output) 
{
    if(prefix.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") != std::string::npos) 
    {
        MYLOG_ERROR(SYLAR_LOG_ROOT()) << "Config invalid name: " << prefix << " : " << node;
        return;
    }
    output.push_back(std::make_pair(prefix, node));
    if(node.IsMap()) 
    {
        for(auto it = node.begin(); it != node.end(); ++it) 
        {
            ListAllMember(prefix.empty() ? it->first.Scalar() : prefix + "." + it->first.Scalar(), it->second, output);
        }
    }
}
功能:

ListAllMember 函数的作用是递归地遍历YAML节点,并将每个节点的路径和节点本身存储到一个列表中。这个函数主要用于将YAML文件中的嵌套结构扁平化,方便后续处理。

详细分析:
  1. 参数说明

    • prefix: 当前节点的路径前缀。初始调用时通常为空字符串。
    • node: 当前要处理的YAML节点。
    • output: 用于存储所有节点的列表,每个节点以 std::pair<std::string, const YAML::Node> 的形式存储,其中 std::string 是节点的路径,YAML::Node 是节点本身。
  2. 函数逻辑

    • 路径合法性检查 :首先检查 prefix 是否只包含合法的字符(字母、数字、下划线和点)。如果包含非法字符,则记录错误日志并返回。
    • 节点存储 :将当前节点的路径和节点本身存储到 output 列表中。
    • 递归处理 :如果当前节点是一个 Map(即YAML中的字典结构),则递归地处理每个子节点。递归时,路径前缀会更新为当前路径加上子节点的键名,并用点号连接。
示例:

假设YAML文件内容如下:

yaml 复制代码
server:
  port: 8080
  host: 127.0.0.1

调用 ListAllMember("", root, all_nodes) 后,all_nodes 列表将包含以下内容:

cpp 复制代码
[
    {"server", YAML::Node},
    {"server.port", YAML::Node},
    {"server.host", YAML::Node}
]

2. LoadFromYaml 函数

cpp 复制代码
void Config::LoadFromYaml(const YAML::Node &root) 
{
    std::list<std::pair<std::string, const YAML::Node> > all_nodes;
    ListAllMember("", root, all_nodes);

    for(auto& i : all_nodes) 
    {
        std::string key = i.first;
        if(key.empty())  continue;

        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        ConfigVarBase::ptr var = LookupBase(key);

        if(var) 
        {
            if(i.second.IsScalar()) {
                var->fromString(i.second.Scalar());
            } else {
                std::stringstream ss;
                ss << i.second;
                var->fromString(ss.str());
            }
        }
    }
}
功能:

LoadFromYaml 函数的作用是从YAML配置文件中加载配置,并将其应用到内存中的配置变量中。它通过调用 ListAllMember 函数将YAML文件中的嵌套结构扁平化,然后遍历所有节点,将配置值应用到相应的配置变量中。

详细分析:
  1. 参数说明

    • root: YAML文件的根节点。
  2. 函数逻辑

    • 扁平化处理 :首先调用 ListAllMember 函数,将YAML文件中的嵌套结构扁平化,存储到 all_nodes 列表中。
    • 遍历节点 :遍历 all_nodes 列表中的每个节点。
    • 键名处理:将键名转换为小写,并检查是否为空。如果为空则跳过。
    • 查找配置变量 :通过 LookupBase 函数查找与键名对应的配置变量。
    • 应用配置值 :如果找到了对应的配置变量,则根据节点的类型(Scalar 或非 Scalar)将配置值应用到配置变量中。
示例:

假设YAML文件内容如下:

yaml 复制代码
server:
  port: 8080
  host: 127.0.0.1

调用 LoadFromYaml(root) 后,程序会查找名为 server.portserver.host 的配置变量,并将 8080127.0.0.1 分别应用到这些变量中。

3. LookupBase 函数

cpp 复制代码
ConfigVarBase::ptr Config::LookupBase(const std::string &name)
{
    auto it = s_datas.find(name);
    return it == s_datas.end() ? nullptr : it->second;
}
功能:

LookupBase 函数的作用是根据给定的名称查找对应的配置变量。

详细分析:
  1. 参数说明

    • name: 要查找的配置变量的名称。
  2. 函数逻辑

    • 查找配置变量 :在 s_datas(可能是一个 std::mapstd::unordered_map)中查找与 name 对应的配置变量。
    • 返回结果 :如果找到则返回对应的配置变量指针,否则返回 nullptr

总结:

  • ListAllMember 函数用于将YAML文件中的嵌套结构扁平化,方便后续处理。
  • LoadFromYaml 函数用于将YAML文件中的配置加载到内存中的配置变量中。
  • LookupBase 函数用于根据名称查找配置变量。

这些函数共同构成了一个从YAML配置文件中加载配置并应用到内存中的配置管理系统。

相关推荐
ryrhhhh2 小时前
矩阵跃动自研技术:小陌GEO动态监测算法,30分钟快速适配大模型更新
人工智能·算法·矩阵
01二进制代码漫游日记2 小时前
动态顺序表的实现(修改)
数据结构·算法
佩奇大王2 小时前
P103 日期问题
java·开发语言·算法
计算机安禾2 小时前
【C语言程序设计】第38篇:链表数据结构(二):链表的插入与删除操作
c语言·开发语言·数据结构·c++·算法·链表
颜酱2 小时前
吃透回溯算法:从框架到实战
javascript·后端·算法
oem1102 小时前
C++中的适配器模式
开发语言·c++·算法
逸Y 仙X2 小时前
文章八:ElasticSearch特殊数据字段类型解读
java·大数据·linux·运维·elasticsearch·搜索引擎
青木川崎2 小时前
设计模式之面试题
java·开发语言·设计模式
空空潍2 小时前
Java核心基础语法:从原理到实战,夯实Java开发基石
java·开发语言