从多类型接口到模板接口

为什么

在一些编码实现中通常都有数据库或者属性接口,用来根据Key存取各种类型的数据,譬如:

class OldImpl
{
public:
    bool has(const std::string& key) const;

    void setAsInt(const std::string& key, int v);
    void setAsDouble(const std::string& key, double v);
    void setAsString(const std::string& key, const std::string& v);

    int  asInt(const std::string& key);
    double asDouble(const std::string& key);
    std::string asString(const std::string& key);
};

实际上只有3种不同功能的接口:是否存在、读取、写入,能否将这些接口转换成对应的模板接口?

目标

在原有接口基础上提供模板接口,如下:

class OldImpl
{
public:
    template
    bool has(const std::string& key) const;

    template
    void set(const std::string& key, const T& v);

    template
    T    as(const std::string& key);
};

使得可以以如下方法使用:

void test() {
    OldImpl impl;
    if (!impl.has("a")) {
        impl.set("a", 'a');
    }
    
    auto x = impl.as("x");
    auto y = impl.as("y");
    auto z = x + y;
    impl.set("z", z);
}

如何实现写入的模板接口

首先,原有的接口是可以写成如下形式的:

    void set(const std::string& key, int v);
    void set(const std::string& key, double v);
    void set(const std::string& key, const std::string& v);

这样的话写入的模板接口可以以如下方式实现:

    template
    void set(const std::string& key, const T& v) {
        set(key, v);
    }

编译时根据v的类型即可找到正确的写入接口,如果调用写入接口时指定T,可以利用C++的自动类型转换/构造来写入指定类型的值。

如何实现读取的模板接口

在实现写入接口时,可以根据函数参数类型的不同区分不同的接口,但是读取的模板接口仅有一个相同类型的参数,根据函数返回值是无法区分的,这时候就需要用到Tag Dispatch;
C++是强类型语言,在名称相同的情况下,可以根据参数类型的不同来区分不同的接口;可以使用类模板定义不同的类型:

template
struct TypeTag {};

采用上述的方式声明类模板,那么TypeTagTypeTag等一系列都是不同的类型:

这时可以将读取接口写成如下方式:

    int as(const std::string& key, TypeTag);
    double as(const std::string& key, TypeTag);
    std::string as(const std::string& key, TypeTag);

而读取接口模板实现如下:

    template
    T  as(const std::string& key) {
        return as(key, TypeTag());
    }

如何实现是否存在接口

通常情况下原本的has接口是直接可用的,如果需要判定某个Key及指定数据类型的值是否存在,可以参照读取接口的实现。

学到的内容

  • Tag Dispatch

你可能感兴趣的:(从多类型接口到模板接口)