配置参数管理类制作

文章介绍了如何设计一个配置管理模块,通过模板类LexicalCast实现字符串与不同类型数据间的转换,包括基本类型、容器类型以及布尔值。同时,通过配置类和单例模式,管理项目中的配置参数并支持文件读写操作。
摘要由CSDN通过智能技术生成

        在项目中经常会用到各种配置,而配置文件中的配置总是以字符的方式存在,则在程序中则需要对各个参数进行解析操作,如下则设计一个直接将配置以字符串读取后解析的配置管理模块。

一、首先需一个可以将字符串和各种数据类型直接转换的类处理。

   配置文件中的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 中实现)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值