文章目录
- p12:复杂类型解析
- 一、方法函数
- 二、结果展示
- p13:复杂类型解析完善
- 一、方法函数
- 二、结果展示
- p14:自定义类型解析
- 一、方法函数
- 二、小结
p12:复杂类型解析
本节内容主要针对完了配置类中对于复杂类型的转换。之前只实现了基础类型之间的转换,而对于容器等复杂类型无法转换,所以本节实现了对于vector
与string
之间的转换方式。
一、方法函数
ConfigVar
之前由于只有基础类型转换,代码里面直接调用lexical_cast
,这样做无法在传入复杂类型时进行解析。因此把lexical_cast
的转化过程函数化,方便在外部针对特定类型的转化。
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;
ConfigVar(const std::string& name, const T& default_value, const std::string& description = "") // 初始化配置名称、参数值以及参数描述
:ConfigvarBase(name,description)
,m_val(default_value) {
}
std::string toString() override { // 将参数值转换为string类型
try {
// return boost::lexical_cast<std::string>(m_val);
return ToStr()(m_val);
} catch(std::exception& e) {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception"
<< e.what() << "convert: " << typeid(m_val).name() << " to string";
}
return "";
}
bool fromString(const std::string& val) override { // 从string转换为参数值
try {
// m_val = boost::lexical_cast<T>(val);
setValue(FromStr()(val));
} catch (std::exception& e) {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception "
<< e.what() << " convert: string to " << typeid(m_val).name();
}
return false;
}
const T getValue() const { return m_val; }
void setValue(const T& v) { m_val = v; }
private:
T m_val;
};
LexicalCast
如果是int float
等基础类型转化,使用下列函数,
// 基础类型转换 把F转换成T类型
template<class F, class T>
class LexicalCast {
public:
T operator()(const F& v) {
return boost::lexical_cast<T>(v);
}
};
[string -> vector]。代码比较清楚,直接看注释就行。
// 复杂类型转换 偏特化[string -> vector]
template<class T>
class LexicalCast<std::string, std::vector<T>> {
public:
std::vector<T> operator()(const std::string& v) {
// 调用YAML中的load函数,接收一个string类型输入,将其转换成node结构
YAML::Node node = YAML::Load(v);
std::vector<T> vec; // 定义结果vec
std::stringstream ss; // 使用字符串流来获取node中的每个string值
for(size_t i = 0; i < node.size(); ++ i) {
ss.str("");
ss << node[i];
vec.push_back(LexicalCast<std::string, T>()(ss.str())); // 这里相当于调用基础类型转换,如果传入的vec里面元素是int类型,那么就是string -> int
}
return vec;
}
};
[vector -> string]
// 复杂类型转换 偏特化[vector -> string]
template<class T>
class LexicalCast<std::vector<T>, std::string> {
public:
std::string operator()(const std::vector<T>& v) {
// 定义一个node容器
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();
}
};
二、结果展示
下面代自定义了一个配置参数项g_vec_value_config
,输出后载入log.yml
文件,更改了g_vec_value_config里面的元素值。
sylar::ConfigVar<std::vector<int>>::ptr g_vec_value_config =
sylar::Config::Lookup("system.vec", std::vector<int>{1,2,3}, "system vec");
void test_config() {
// SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before:" << g_int_value_config->getValue();
// SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before:" << g_float_value_config->toString();
auto v = g_vec_value_config->getValue();
auto res = g_vec_value_config->toString();
std::cout << res << std::endl;
for(auto& i : v) {
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before:" << i;
}
YAML::Node root = YAML::LoadFile("/root/Web-learning/sylar/bin/conf/log.yml");
sylar::Config::LoadFromYaml(root);
v = g_vec_value_config->getValue();
for(auto& i : v) {
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "after:" << i;
}
// SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "after:" << g_int_value_config->getValue();
// SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "after:" << g_float_value_config->toString();
}
int main(int argc, char** argv) {
// test_yaml();
test_config();
return 0;
}
p13:复杂类型解析完善
本节内容是对上一节STL容器的完善,新增了对list、map、set、unordered_map、unordered_set
的支持。由于本节的代码内容和上一节比较重复,所以简单记录一下部分内容。
一、方法函数
map
注意循环的迭代方式与vector
等容器不同,然后目前对于map
里面的类型只支持基本类型的转换,所以后期需要按我们目前做的方式让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);
std::map<std::string,T> vec;
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;
}
};
宏定义
测试时代码重复比较大,通过下面两个宏函数简化。
#define XX(g_var,name,prefix) \
{ \
auto v = g_var->getValue(); \
for(auto& i : v) { \
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << #prefix " " #name ": " << i; \
} \
}
#define XX_M(g_var,name,prefix) \
{ \
auto v = g_var->getValue(); \
for(auto& i : v) { \
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << #prefix " " #name ": {" << i.first << " : " << i.second << "}"; \
} \
}
二、结果展示
p14:自定义类型解析
本节内容主要讲解了Config
解析自定义类型,和上面两节一样,处理复杂类型或者自定义类型的关键是片特化函数LexicalCast
,下面是一些简单比较记录。
一、方法函数
之前代码在解析yml
文件时,如果遇到相同key
但是类型不同,会直接忽略。比如下列代码,key:system.port
的值存在两种类型,当我们使用Config::Lookup(system.port
)`查找时,不知道最后找到的是谁,所以程序应该在这里报错提示。
sylar::ConfigVar<int>::ptr g_int_value_config =
sylar::Config::Lookup("system.port", (int)8080, "system port");
sylar::ConfigVar<float>::ptr g_int_valuex_config =
sylar::Config::Lookup("system.port", (float)8080, "system port");
Lookup
对该函数进行修改,核心思想就是利用dynamic_pointer_cast
,如果相同key
的类型不同,则会转换失败返回nullptr
auto it = s_datas.find(name); // 直接从配置容器中查找
if(it != s_datas.end()) {
auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
if(tmp) {
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name = " << name << " exists";
} else {
SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name = " << name << " exists but type not " << typeid(T).name();
return nullptr;
}
}
再次运行可以看到已经有了报错提示
自定义类型
以下我们定义了一个Person
类,希望能从log.yml
文件中解析出相应的配置项。
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();
}
};
第一步肯定是去书写对应的片特化函数LexicalCast
,函数里面的内容无非就是YAML::NODE
类型与自定义类型之间的值传递。
namespace sylar {
// 复杂类型转换 偏特化[string -> Person]
template<>
class LexicalCast<std::string, Person> {
public:
Person operator()(const std::string& v) {
// 调用YAML中的load函数,接收一个string类型输入,将其转换成node结构
YAML::Node node = YAML::Load(v);
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) {
// 定义一个node容器
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();
}
};
}
来一个测试用例
// 1.自定义类型
sylar::ConfigVar<Person>::ptr g_person =
sylar::Config::Lookup("class.person", Person(), "system person");
// log.yml
class:
person:
name: ikun
age: 25
sex: true
输出结果:
自定义类型与STL的嵌套
设计一些复杂的用例
// 2.自定义类型与STL嵌套
sylar::ConfigVar<std::map<std::string, Person>>::ptr g_person_map =
sylar::Config::Lookup("class.map", std::map<std::string, Person>(), "system map");
// 3.更加复杂的类型map<string,vector<Person>>
sylar::ConfigVar<std::map<std::string, std::vector<Person>>>::ptr g_person_vec_map =
sylar::Config::Lookup("class.vec_map", std::map<std::string, std::vector<Person>>(), "system vec_map");
// log.yml
map:
person1:
name: ikun2
age: 252
sex: true
person2:
name: ikun3
age: 152
sex: false
vec_map:
vec1:
- name: ikun
age: 25
sex: true
- name: ikun2
age: 252
sex: true
vec2:
- name: ikun4
age: 225
sex: true
- name: ikun21
age: 2152
sex: true
输出结果:
二、小结
整个配置模块到这基本完成,这几节的内容目的就是尽可能的去解析不同类型的配置内容,所以只要12节视频基本弄懂,后面两节的内容也没什么大问题。建议自己跟着视频写好代码后可以把解析过程每一步的内容打印出来,看和自己想的是否一样。