1. Config

1.1 ConfigVarBase

配置变量基类:每个配置的变量都有变量名name和变量描述description两个基础成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ConfigVarBase {
public:
typedef std::shared_ptr<ConfigVarBase> ptr;
ConfigVarBase(const std::string& name, const std::string& description = "")
:m_name(name)
, m_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& val) = 0;
virtual std::string getTypeName() const = 0;
protected:
std::string m_name;
std::string m_description;
};

1.2 ConfigVar

  • 成员变量T m_val:保存变量本身
  • 成员变量std::map<uint64_t, on_change_cb> m_cbs:回调函数,变量变化调用
  • 父类成员变量name:变量名
  • 父类成员变量description:描述变量作用
  • 成员方法toString():将m_val转换为YAML String
  • 成员方法fronString():将YAML String转换为m_val
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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_cb;

ConfigVar(const std::string& name
, const T& default_value
, const std::string& description = "")
:ConfigVarBase(name, description)
, m_val(default_value) {
}

std::string toString() override {
try {
return boost::lexical_cast<std::string>(m_val);
RWMutexType::ReadLock lock(m_mutex);
return ToStr()(m_val);
}
catch (std::exception& e) {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception "
<< e.what() << " convert: " << TypeToName<T>() << " to string"
<< " name=" << m_name;
}
return "";
}

bool fromString(const std::string& val) override {
try {
setValue(FromStr()(val));
}
catch (std::exception& e) {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception "
<< e.what() << " convert: string to " << TypeToName<T>()
<< " name=" << m_name
<< " - " << val;
}
return false;
}

const T getValue() {
RWMutexType::ReadLock lock(m_mutex);
return m_val;
}

void setValue(const T& v) {
{
RWMutexType::ReadLock lock(m_mutex);
if (v == m_val) {
return;
}
for (auto& i : m_cbs) {
i.second(m_val, v);
}
}
RWMutexType::WriteLock lock(m_mutex);
m_val = v;
}

std::string getTypeName() const override { return TypeToName<T>(); }

uint64_t addListener(on_change_cb cb) {
static uint64_t s_fun_id = 0;
RWMutexType::WriteLock lock(m_mutex);
++s_fun_id;
m_cbs[s_fun_id] = cb;
return s_fun_id;
}
void delListener(uint64_t key) {
RWMutexType::WriteLock lock(m_mutex);
m_cbs.erase(key);
}

on_change_cb getListener(uint64_t key) {
RWMutexType::ReadLock lock(m_mutex);
auto it = m_cbs.find(key);
return it == m_cbs.end() ? nullptr : it->second;
}
void clearListener() {
RWMutexType::WriteLock lock(m_mutex);
m_cbs.clear();
}
private:
RWMutexType m_mutex;
T m_val;
//变更回调函数组, uint64_t key,要求唯一,一般可以用hash
std::map<uint64_t, on_change_cb> m_cbs;
};

1.3 Config

  • 静态成员变量ConfigVarMap:定义为unordered_map<std::string, ConfigVarBase::ptr>,保存了全部配置文件的属性名以及对应的属性值
  • 静态成员函数Lookup():根据参数名,返回一个ConfigVar<T>配置参数
    • 如果已存在参数名
      • 如果参数名和类型都相同,返回该参数
      • 如果类型名不相同,返回空指针
    • 如果不存在参数名,创建ConfigVar<T>并返回
  • 成员函数LoadFromYaml():从YAML格式文档加载到一个
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
class Config {
public:
typedef std::unordered_map<std::string, ConfigVarBase::ptr> ConfigVarMap;
typedef RWMutex RWMutexType;
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name,
const T& default_value, const std::string& description = "") {
RWMutexType::WriteLock lock(GetMutex());
auto it = GetDatas().find(name);
if (it != GetDatas().end()) {
auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
if (tmp) {
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists";
return tmp;
}
else {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists but type not "
<< TypeToName<T>() << " real_type=" << it->second->getTypeName()
<< " " << it->second->toString();
return nullptr;
}
}

if (name.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678")
!= std::string::npos) {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name;
throw std::invalid_argument(name);
}

typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
GetDatas()[name] = v;
return v;
}

template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
RWMutexType::ReadLock lock(GetMutex());
auto it = GetDatas().find(name);
if (it == GetDatas().end()) {
return nullptr;
}
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}

static void LoadFromYaml(const YAML::Node& root);
static void LoadFromConfDir(const std::string& path, bool force = false);
static ConfigVarBase::ptr LookupBase(const std::string& name);
static void Visit(std::function<void(ConfigVarBase::ptr)> cb);

private:
static ConfigVarMap& GetDatas() {
static ConfigVarMap s_datas;
return s_datas;
}

static RWMutexType& GetMutex() {
static RWMutexType s_mutex;
return s_mutex;
}
};

2. LexicalCast

2.1 泛型类型转换

  • 把类型F转换为类型T
1
2
3
4
5
6
7
template<class F, class T>
class LexicalCast {
public:
T operator()(const F& v) {
return boost::lexical_cast<T>(v);
}
};

2.2 偏特化类型转换

下面是几个例子方便理解,其他的大同小异不再列举

  • YAML string转换为vector<T>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 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()));
}
return vec;
}
};
  • YAML string转换为set<T>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<class T>
class LexicalCast<std::string, std::set<T> > {
public:
std::set<T> operator()(const std::string& v) {
YAML::Node node = YAML::Load(v);
typename std::set<T> vec;
std::stringstream ss;
for (size_t i = 0; i < node.size(); ++i) {
ss.str("");
ss << node[i];
vec.insert(LexicalCast<std::string, T>()(ss.str()));
}
return vec;
}
};
  • set<T>转换为YAML string
1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T>
class LexicalCast<std::set<T>, std::string> {
public:
std::string operator()(const std::set<T>& v) {
YAML::Node node(YAML::NodeType::Sequence);
for (auto& i : v) {
node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
}
std::stringstream ss;
ss << node;
return ss.str();
}
};

3. Config使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
void test_config()
{
// 使用Lookup静态方法创建一个system.port
sylar::ConfigVar<int>::ptr g_int_value_config =
sylar::Config::Lookup("system.port", (int)8080, "system port");
// 使用Lookup静态方法创建一个system.userid
sylar::ConfigVar<std::map<std::string, std::string>>::ptr g_int_map_config =
sylar::Config::Lookup("system.userid", std::map<std::string, std::string>{{"xuan", "0001"}}, "system str int map");

// 使用Lookup静态方法用该配置名查找对应的变量并输出
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "system.port: " << sylar::Config::Lookup<int>("system.port")->toString();
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "system.userid: " << sylar::Config::Lookup<std::map<std::string, std::string>>("system.userid")->toString();
}