通常来说,出现不同数据格式转换的场景,比如给定一个整数,将它与每周的星期名称映射,通常会采用if-else或switch语句,但是这种语句存在两个问题
(1)代码太长,逻辑重复冗余,复杂度高
(2)可维护性低,耦合性强,每新增一个流程分支,就要在函数代码中新添加一个判断语句。
这里介绍一种通用的表驱动实现方法替代以上方式。
要实现一个通用的表驱动模式需要解决两个问题:
(1)如何在表中注册不同类型的执行函数?
(2)在表中找到对应的执行函数之后如何调用执行函数。由于每个函数的形参不尽相同,如何用统一的方式调用?
解决思路:
问题1:可以使用boost::Any解决
问题2:可以采用C++11的可变模板参数解决
通用的表驱动C++11实现支持各种类型的key,执行函数支持普通函数、函数对象、lamda表达式和成员函数。
.hpp文件
#ifndef TABLE_DRIVER_APPROACH_HPP_
#define TABLE_DRIVER_APPROACH_HPP_
#include<iostream>
#include<array>
#include<map>
#include<functional>
#include<type_traits>
#include<boost/any.hpp>
#include<typeinfo>
template<typename Key>
class TableDriver
{
public:
template<typename... Args,typename Func>
void Register(const Key& key,Func &&func)
{
typedef typename std::result_of<Func(Args...)>::type rettype;
auto f=std::function<rettype(Args && ...)>([=](Args &&... args){return func(std::forward<Args>(args)...); });
m_map[key]=f;
}
template<typename R=void,typename... Args>
R Execute(const Key& key,Args &&... args)
{
auto it=m_map.find(key);
if(it==m_map.end())
{
return R();
}
auto f=boost::any_cast<std::function<R(Args && ...)> >(it->second);
return f(std::forward<Args>(args)...);
}
template<typename R=void> //无参数
R Execute(const Key& key)
{
auto it=m_map.find(key);
if(it==m_map.end())
{
return R();
}
auto f=boost::any_cast<std::function<R()> >(it->second);
return f();
}
private:
std::map<Key,boost::any> m_map;
};
#endif
测试代码:
#include"TableDriveApproach.hpp"
TableDriver<int> dv;
const std::string GetDayName(const int& day)
{
return dv.Execute<std::string>(day);
}
int main()
{
dv.Register(0,[]()->std::string{return "星期日";});
dv.Register(1,[]()->std::string{return "星期一";});
dv.Register(2,[]()->std::string{return "星期二";});
dv.Register(3,[]()->std::string{return "星期三";});
dv.Register(4,[]()->std::string{return "星期四";});
dv.Register(5,[]()->std::string{return "星期五";});
dv.Register(6,[]()->std::string{return "星期六";});
int day=3;
std::cout<<GetDayName(day)<<std::endl;
return 0;
}