将字符串和枚举映射到模板化类型

时间:2015-09-17 17:57:58

标签: c++ templates c++11 enums

我的函数 foo 有两个参数 - 一个字符串 str ,一个强类型的枚举 \ n> ENUM1 。它应该实例化一个类模板,其模板参数依赖于str和ENUM1。如何在不使用多个if..then..else语句的情况下实现此目的?

void foo(std::string str, ENUM1 enumVal) {
    if (str == "str1" && enumVAL == ENUM1::VAL1)
           ExampleClass<STR1TYPE, VAL1TYPE> instance;
    else if (str == "str1" && enumVAL == ENUM1::VAL2)
           ExampleClass<STR1TYPE, VAL2TYPE> instance;
    else if (str == "str2" && enumVAL == ENUM1::VAL1)
           ExampleClass<STR2TYPE, VAL1TYPE> instance;
    ...
}

我无法改变ExampleClass的设计方式,也无法更改进入的参数类型。我讨厌if then else以获取两个参数值的所有排列。

我在考虑保留std::unordered_map<std::pair<std::string, ENUM>, ExampleClassBase>,但这意味着占用内存以节省代码的可读性。

这样做有什么方法吗?

1 个答案:

答案 0 :(得分:1)

嗯,模板和类型特征魔术的一个小例子。

首先 - 单个案例的功能对你很有用:

template <typename StrType, typename EnumType>
void fooSingleCase()
{
    std::cout << "In: " << __PRETTY_FUNCTION__ << "\n"; 
    ExampleClass<StrType, EnumType> instance;
    // ...
}

如果您需要为不同的StrType / EnumType对提供不同的代码 - 您可以稍微修改一下:

template <typename StrType, typename EnumType>
void fooSingleCaseImplementation(ExampleClass<StrType, EnumType>& instance)
{
   // most default implementation
}
void fooSingleCaseImplementation(ExampleClass<STR1TYPE, VAL2TYPE>& instance)
{
   // some specific implementation
}


template <typename StrType, typename EnumType>
void fooSingleCase()
{
    std::cout << "In: " << __PRETTY_FUNCTION__ << "\n"; 
    ExampleClass<StrType, EnumType> instance;
    fooSingleCaseImplementaction(instance);
}

但是如何从foo转到此fooSingleCase。首先,您需要使用值连接类型 - 您需要类型特征技术:

template <typename StrType>
struct StrTraits
{
    static const std::string pattern;
}; 
template <typename EnumType>
struct ENUM1Traits
{
    static const ENUM1 pattern;
};

特征实例:

template <>
const std::string StrTraits<STR1TYPE>::pattern = "str1";
template <>
const std::string StrTraits<STR2TYPE>::pattern = "str2";

template <>
const ENUM1 ENUM1Traits<VAL1TYPE>::pattern = ENUM1::VAL1;
template <>
const ENUM1 ENUM1Traits<VAL2TYPE>::pattern = ENUM1::VAL2;
template <>
const ENUM1 ENUM1Traits<VAL3TYPE>::pattern = ENUM1::VAL3;

所以 - 我们差不多完成了。我们现在需要制作模板魔术:

首先 - 假设您已经获得了EnumType - 您只需要选择strType - 所以 - 开始使用特征:

template <typename EnumType>
void fooFixedEnumCase(std::string strVal)
{
    std::cout << strVal << " not found!\n";
}

template <typename EnumType, typename Str1Type, typename ...StrType>
void fooFixedEnumCase(std::string strVal)
{
    if (StrTraits<Str1Type>::pattern == strVal)
        fooSingleCase<EnumType, Str1Type>();
    else
        fooFixedEnumCase<EnumType, StrType...>(strVal);
}

为了选择enumType然后strType - 我们需要两组可变参数模板类型 - 所以让我们使用带有成员函数模板的类模板:

template <typename ...EnumType>
struct Enum1Switch;

template <typename EnumType1, typename ...EnumType>
struct Enum1Switch<EnumType1, EnumType...>
{
    template <typename ...StrType>
    static void strSwitch(std::string str, ENUM1 enumVal)
    {
        if (ENUM1Traits<EnumType1>::pattern == enumVal)
            fooFixedEnumCase<EnumType1, StrType...>(str);
        else
            Enum1Switch<EnumType...>::template strSwitch<StrType...>(str, enumVal);
    }
};

template <>
struct Enum1Switch<>
{
    template <typename ...StrType>
    static void strSwitch(std::string str, ENUM1 enumVal)
    {
        std::cout << static_cast<int>(enumVal) << " not found!\n";
    }
};

然后你的foo将如此简单:

void foo(std::string str, ENUM1 enumVal) {

    Enum1Switch<VAL1TYPE, VAL2TYPE, VAL3TYPE>
        ::strSwitch<STR1TYPE, STR2TYPE>(str, enumVal);
}

ideone

中的示例

问题 - 如何使用新类型扩展此机制。喜欢STRTYPENEW,VALTYPENEW。只需要几步:

新特征:

template <>
const std::string StrTraits<STRTYPENEW>::pattern = "newstr";
template <>
const ENUM1 ENUM1Traits<VALTYPENEW>::pattern = ENUM1::NEW_VAL;

foo只是一个小小的变化:

void foo(std::string str, ENUM1 enumVal) {

    Enum1Switch<VAL1TYPE, VAL2TYPE, VAL3TYPE, VALTYPENEW>
    //                                      ^^^^^^^^^^^^
        ::strSwitch<STR1TYPE, STR2TYPE, STRTYPENEW>(str, enumVal);
    //                                ^^^^^^^^^^^^  
}

最后一条评论 - 如果你把所有这些函数都内联起来 - 为这个模板魔法生成的代码(我的意思是汇编程序代码)和你的初始if / else方法是非常相同的。