在项目中经常会用到各种配置,而配置文件中的配置总是以字符的方式存在,则在程序中则需要对各个参数进行解析操作,如下则设计一个直接将配置以字符串读取后解析的配置管理模块。
一、首先需一个可以将字符串和各种数据类型直接转换的类处理。
配置文件中的string可以直接转换成int,double,std::vector<T> 等各种类型数据,则以模板的方式实现,以偏特化操作实现特殊类型之间的转换,实现一个lexcialCast.hpp 以实现该功能。
#pragma once
#include<string>
#include<vector>
#include<stdlib.h>
template<class F, class T>
class LexicalCast { };
template<>
class LexicalCast<std::string, int>
{
public:
float operator()(const std::string& val)
{
return atoi(val.c_str());
}
};
template<>
class LexicalCast<std::string, float>
{
public:
float operator()(const std::string& val)
{
return (float)atof(val.c_str());
}
};
template<>
class LexicalCast<std::string, double>
{
public:
double operator()(const std::string& val)
{
return (float)atof(val.c_str());
}
};
template<class T>
class LexicalCast<T, std::string> {
public:
std::string operator()(const T& val)
{
return std::to_string(val);
}
};
template<>
class LexicalCast<std::string, std::string>
{
public:
std::string operator()(const std::string& val)
{
return val;
}
};
template<>
class LexicalCast<bool, std::string>
{
public:
std::string operator()(const bool& val)
{
return val ? "true" : "false";
}
};
template<>
class LexicalCast<std::string, bool> {
public:
bool operator()(const std::string& val)
{
return val.compare("true") == 0 ? true: false;
}
};
template<class T>
class LexicalCast<std::vector<T>, std::string>
{
public:
std::string operator()(const std::vector<T>& veVal)
{
std::string ss;
ss = "{ ";
for (auto& iter : veVal)
{
ss += LexicalCast<T, std::string>()(iter);
ss += ",";
}
ss = ss.erase(ss.size() - 1);
ss += " }";
return ss;
}
};
template<class T>
class LexicalCast<std::string, std::vector<T>>
{
public:
std::vector<T> operator()(const std::string& val)
{
typename std::vector<T> vec;
auto Replace = [](const std::string& str, char from, char to)->std::string
{
int len = str.length();
std::string newStr;
for (int i = 0; i < len; i++)
{
if (str[i] == from)
{
newStr += to;
}
else
{
newStr += str[i];
}
}
return newStr;
};
auto SplitString = [](const std::string& s, const std::string& c)->std::vector<std::string>
{
std::vector<std::string> v;
std::string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (std::string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if (pos1 != s.length())
v.push_back(s.substr(pos1));
return v;
};
std::string newStr;
newStr = Replace(val, '{', ' ');
newStr = Replace(newStr, '}', ' ');
auto strVec = SplitString(newStr, ",");
for(auto & substr : strVec)
{
vec.push_back(LexicalCast<std::string,T>()(substr));
}
return vec;
}
};
二、有了可以转换数据的接口后,这需要一个类来处理各种参数,实现以一个config.hpp来对具体每个参数进行管理。
一个参数的基本状态有:名称,值,备注描述 ,以ConfigVarBase类来描述每个一个参数的基本特性,其派生类ConfigVar 则是具体实现可以对每一个参数的字符和类型值之间的转换管理。ConfigConfig类则是真正对外开放使用的接口类。
#pragma once
#include<mutex>
#include<shared_mutex>
#include<unordered_map>
#include<string>
#include<sstream>
#include"lexcialCast.hpp"
class ConfigVarBase
{
public:
typedef std::shared_ptr<ConfigVarBase> ptr;
public:
ConfigVarBase(const std::string& name, const std::string& description)
:m_strName(name),
m_strDescription(description) {
}
const std::string getName() const {
return m_strName;
}
const std::string getDescription() const {
return m_strDescription;
}
virtual ~ConfigVarBase() { }
virtual std::string toString() = 0;
virtual bool fromString(const std::string& val) = 0;
private:
std::string m_strName;
std::string m_strDescription;
};
template<typename T,class
ToStr = LexicalCast<T,std::string>,
class FromStr = LexicalCast<std::string,T>>
class ConfigVar : public ConfigVarBase
{
public:
typedef std::shared_ptr<ConfigVar> ptr;
ConfigVar(const std::string& name, const T& defaultVal, const std::string& desciption)
:ConfigVarBase(name, desciption), m_nVar(defaultVal) {
}
virtual std::string toString() override
{
std::lock_guard<std::mutex> lk(m_shMutex);
return ToStr()(m_nVar);
}
virtual bool fromString(const std::string& val) override
{
setValue(FromStr()(val));
return true;
}
const T getValue()
{
std::lock_guard<std::mutex> lk(m_shMutex);
return m_nVar;
}
void setValue(const T& val)
{
std::lock_guard<std::mutex> lk(m_shMutex);
m_nVar = val;
}
private:
T m_nVar;
std::mutex m_shMutex;
};
class Config
{
public:
typedef std::shared_ptr<Config> ptr;
typedef std::unordered_map<std::string, ConfigVarBase::ptr> configVarMap;
//用以初始化参数变量,如果存在则返回,如果不存在则创建
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> lk(GetMutex());
auto it = GetDatas().find(name);
if (it != GetDatas().end()) {
auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
if (tmp) {
return tmp;
}
else
{
return nullptr;
}
}
//限制参数名称命名方式
if (name.find_first_not_of("abcdefghikjlmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._012345678")
!= std::string::npos) {
throw std::invalid_argument(name);
}
typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
GetDatas()[name] = v;
return v;
}
//用以参数名查询参数是否已经存在,存在返回,不存在直接返回null
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
std::lock_guard<std::mutex> lk(GetMutex());
auto it = GetDatas().find(name);
if (it == GetDatas().end()) {
return nullptr;
}
return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
}
private:
static configVarMap& GetDatas() {
static configVarMap s_datas;
return s_datas;
}
static std::mutex& GetMutex() {
static std::mutex m_shMutex;
return m_shMutex;
}
};
三、再添加一个单例生成类,使每一个配置都可以是一个单例管理。
#ifndef SINGLETON_H__
#define SINGLETON_H__
#include <mutex>
template<class T>
class Singleton
{
public:
Singleton()
{
}
template <typename... Args>
static T* GetInstance(Args&&... args)
{
if (m_pInstance == nullptr) //double check
{
static std::mutex m_Mutex;
std::lock_guard<std::mutex> lock(m_Mutex);
if (m_pInstance == nullptr)
{
m_pInstance = new T(std::forward<Args>(args)...);
}
}
return m_pInstance;
}
private:
static T* m_pInstance;
};
template<class T>
T* Singleton<T>::m_pInstance = nullptr;
#endif
四、可以实现一个所有参数管理,对所有的参数进行管理。
#pragma once
#include"config.h"
#include"Singleton.h"
class CConfigParam : public Singleton<CConfigParam >
{
public:
void Init()
{
Threshold=Config::Lookup("Threshold", 4.01, "Params calc threshold");
ThreadNum=Config::Lookup("ThreadNum", 24, "processor thread num");
PriceVal= Config::Lookup("PriceVal", 0.32, "price value");
}
public:
ConfigVar<float>::ptr Threshold;
ConfigVar<int>::ptr ThreadNum;
ConfigVar<double>::ptr PriceVal;
};
五、最后则是直接对各个参数进行使用了,例如直接解析ini 文件或者xml 等类型文件,将其字串读取后直接赋值给CConfigParam类中的对应参数直接进行解析,同样也可以将值直接设置给对应参数使其转换成string类型,可以反写回配置文件中。
CConfigParam::GetInstance()->Threshold->fromString("0.56");
后续其它地方则直接使用
CConfigParam::GetInstance()->Threshold->getValue()
就可以获取到参数中的值。
同样可以使用
CConfigParam::GetInstance()->Threshold->toString()
直接按照配置的格式转换成需要的类型的字符string(lexcialCast.hpp 中实现)。