【转载】C++11 改进我们的模式之改进表驱动模式

427 阅读4分钟

参考原文地址:(原创) C++11 改进我们的模式之改进表驱动模式

我对文章的格式和错别字进行了调整,并标注出了重要的部分。以下是正文。

正文

所谓表驱动法(Table-Driven Approach),简单讲是指用查表的方法获取值。表驱动是将一些通过较为复杂逻辑语句来得到数据信息的方式,通过查询表的方式来实现,将数据信息存放在表里。对于消除长的 switch-case 和 if-else-if 语句来说很有效,比如下面的代码:

待优化的例子

string GetDayName(int day)
{
    string dayName;
    if (day == 1)
    {
        dayName = "星期一";
    }
    else if (day == 2)
    {
        dayName = "星期二";
    }
    else if (day == 3)
    {
        dayName = "星期三";
    }
    else if (day == 4)
    {
        dayName = "星期四";
    }
    else if (day == 5)
    {
        dayName = "星期五";
    }
    else if (day == 0)
    {
        dayName = "星期日";
    }
}

这样的主要问题是:

  • 代码太长,逻辑重复,复杂度高;
  • 可维护性低,当新增一个流程分支时就要添加一个判断语句。

表驱动的改进

通过表驱动就一两条语句就可以代替上面长长的 if-else 语句。

string GetDayName(int day)
{
    string dayNames[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
    return dayNames[day]
}

这样不仅代码看起来简明,而且后面如果增加新的数值的话维护起来也更简单、方便

表驱动的缺陷

表驱动法虽然对于消除长的 if-else 语句、提高代码质量很有用,但是一般的表驱动难以重用,即这个类的表驱动难以被其它类的表驱动所重用,因为其它类的表驱动逻辑和当前类的逻辑并不一定相同,具体来说是逻辑分支的函数不尽相同,这个问题导致表驱动模式的实现不够通用。

通用的表驱动模式

面临的问题

如果要做一个通用的表驱动模式就会面临两个问题

  • 第一个问题是如何在表中注册各种类型的执行函数,这些执行函数形参不尽相同,用 function 是不行的,因为 function 的类型在一开始就定下来了;

  • 第二个问题是表找到了相应的执行函数之后调用该函数的问题,因为每个函数的形参不尽相同,如何以一种统一的方式去调用也是一个问题。

问题的解决

  • 问题二比较好解决,用 C++11 的可变参数模板即可;

  • 问题一需要通过类型擦除来解决,这要用到这个链接中实现的 Any 做这个事情。C++11 版本的表驱动模式,支持各种类型的 key ,执行函数支持普通函数、函数对象、lambda 表达式和成员函数

#include <functional>
#include <type_traits>

template <typename Key>
class TableDriver
{
public:
    template <typename... Args, typename Func>
    void Register(Key &&key, Func &&func)
    {
        //typedef decltype(std::declval<Func>()(std::declval<Args>()...)) rettype;
        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(Key &&key, Args &&...args)
    {
        auto it = m_map.find(key);
        if (it == m_map.end())
            return (R) nullptr;

        auto f = it->second.AnyCast<std::function<R(Args && ...)>>();
        return f(std::forward<Args>(args)...);
    }

    //不带参数的执行
    template <typename R>
    R Execute(Key &&key)
    {
        auto it = m_map.find(key);
        if (it == m_map.end())
            return (R) nullptr;

        auto f = it->second.AnyCast<std::function<void()>>();
        return f();
    }

private:
    std::map<Key, Any> m_map;
};

测试代码

struct MyStruct2
{
    void g(int x)
    {
        cout << x << endl;
    }

    void g1()
    {
        cout << 2 << endl;
    }
};

struct MyStruct3
{
    void operator()(int x) const
    {
        cout << x + 1 << endl;
    }
};

void TestTbDriver()
{
    TableDriver<int> dv;
    
    auto f = [](int a, int b)
    { cout << a + b << endl; };
    auto fuc = [](int a)
    { cout << a << endl; };
    auto fuc1 = []()
    { cout << 1 << endl; };

    dv.Register<decltype(f), int, int>(1, f); //带参数的 lambda 表达式
    dv.Register<decltype(fuc), int>(2, fuc);
    dv.SimpleRegister<decltype(fuc1)>(22, fuc1); //不带参数的 lambda 表达式

    MyStruct2 st2;
    dv.Register<int>(4, &st2, &MyStruct2::g); //带参数的成员函数
    MyStruct3 st3;
    dv.Register<decltype(st3), int>(11, st3);    //函数对象
    dv.SimpleRegister(44, &st2, &MyStruct2::g1); //不带参数的成员函数

    int a = 3, b = 4;
    dv.Execute(2, a);
    dv.Execute(1, a, b);
    dv.Execute(22);
    dv.Execute(44);
    dv.Execute(11, a);
}

测试结果

image.png

原作者的更新

做一个更新,对代码进行简化和优化,更少的代码,并且可以带返回值了

代码中用到的 Any 就是博客 中的 Any

#include "Any.hpp"
template <typename Key>
class TableDriver
{
public:
    template <typename... Args, typename Func>
    void Register(Key &&key, Func &&func)
    { //typedef decltype(std::declval<Func>()(std::declval<Args>()...)) rettype;		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(Key && key, Args && ...args)
        {
            auto it = m_map.find(key);
            if (it == m_map.end())
                return (R) nullptr;
            auto f = it->second.AnyCast<std::function<R(Args && ...)>>();
            return f(std::forward<Args>(args)...);
        }
        //不带参数的执行	template <typename R>	R Execute(Key && key)	{		auto it = m_map.find(key);		if (it == m_map.end())			return (R)nullptr;
        auto f = it->second.AnyCast<std::function<void()>>();
        return f();
    }

private:
    std::map<Key, Any> m_map;
};

测试代码:

struct Tdd
{
    int Test(int x) { return x + 2; }
};
void TestDriver()
{
    TableDriver<string> dv;
    dv.Register("aa", []
                { cout << "aa test;" << endl; });
    dv.Execute("aa");
    int y = 0;
    dv.Register<int, int>("aa", [](int a, int b)
                          { return a + b; });
    auto t = dv.Execute<decltype(y)>("aa", 3, 4);
    Tdd a;
    dv.Register<int>("aa", [&a](int x)
                     { return a.Test(x); });
    auto t1 = dv.Execute<int>("aa", 3);
    dv.Register<string>("aa", [](string x)
                        { return x; });
    auto t2 = dv.Execute<string>(std::move(string("aa")), std::move(string("test")));
}