为元组/参数包中的每种类型生成开关案例

问题描述 投票:0回答:1

我试图弄清楚如何使用模板“magic”为元组中的每种类型自动生成 switch 语句。这是我的示例代码:

#include <string>

enum class AttributeID
{
    ATTRIBUTE1,
    ATTRIBUTE2,
    ATTRIBUTE3
};

struct ATTRIBUTE1
{
    static constexpr auto kID = AttributeID::ATTRIBUTE1;
    using Value = unsigned int;
    
    Value v;
};

struct ATTRIBUTE2
{
    static constexpr auto kID = AttributeID::ATTRIBUTE2;
    using Value = char;
    
    Value v;
};

struct ATTRIBUTE3
{
    static constexpr auto kID = AttributeID::ATTRIBUTE3;
    using Value = std::string;
    
    Value v;
};

class Attributes
{
public:
    using ID = AttributeID;
    
    template <typename T>
    auto& get()
    {
        return std::get<T>(impl_).v;
    }
    
private:
    std::tuple<ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3> impl_;
};

template <typename Attribute>
void foodleAttribute(Attribute& attr, unsigned int data)
{
    //...
    //This template will be specialized based upon the type of Attribute
}

void fiddleAttribute(Attributes& attrs, Attributes::ID id, unsigned int data)
{
    switch(id)
    {
    case ATTRIBUTE1::kID:
        foodleAttribute(attrs.get<ATTRIBUTE1>(), data);
        break;
    case ATTRIBUTE2::kID:
        foodleAttribute(attrs.get<ATTRIBUTE2>(), data);
        break;
    case ATTRIBUTE3::kID:
        foodleAttribute(attrs.get<ATTRIBUTE3>(), data);
        break;
    }
}

我想重写

fiddleAttribute
函数

template <typename Attributes>
void fiddleAttribute(Attributes& attrs, typename Attributes::ID id, unsigned int data)
{
    //Generate a switch case (or equivalent) for each type (its kID constant) in the Attributes tuple
}

这样它会生成与我编写的显式 switch 语句等效的内容(打开

id
,调用模板化
foodleAttribute
函数)。

c++ templates switch-statement c++17
1个回答
0
投票

假设您的枚举类从 0 开始,您可以使用带有折叠表达式的

std::index_sequence

template <std::size_t... Is>
void fiddleAttributeImpl(Attributes& attrs, Attributes::ID id, unsigned int data, std::index_sequence<Is...>)
{
    void(((static_cast<int>(id) == Is 
            && (foodleAttribute(attrs.get<std::tuple_element_t<Is, Types>>(), data), false)) || ...));
}

void fiddleAttribute(Attributes& attrs, Attributes::ID id, unsigned int data)
{
    return fiddleAttributeImpl(attrs, id, data, std::make_index_sequence<std::tuple_size_v<Types>>{});
}

要折叠的表达式是

static_cast<int>(id) == Is 
            && (foodleAttribute(attrs.get<std::tuple_element_t<Is, Types>>(), data), true)

仅当运行时间

&&
等于索引
id
时才会执行
Is
之后的部分。逗号表达式使子表达式计算为 true 并短路以避免进一步比较。我们使用
std::tuple_element_t
从类型列表中获取第
Is
类型。

请参阅编译器资源管理器:https://godbolt.org/z/c1xfThz65


另外,一种本质上与 switch 语句更相似的方法是构建一个 lambda 数组,然后在运行时对其进行索引。

template <std::size_t... Is>
void fiddleAttributeImpl(Attributes& attrs, Attributes::ID id, unsigned int data, std::index_sequence<Is...>)
{
    constexpr std::array<void(*)(Attributes&, unsigned int), std::tuple_size_v<Types>>
    jumpTable{
        [](Attributes& attrs, unsigned int data){foodleAttribute(attrs.get<std::tuple_element_t<Is, Types>>(), data);}
        ...
    };
    
    jumpTable[static_cast<int>(id)](attrs, data);
}

请参阅编译器资源管理器:https://godbolt.org/z/6jnGszvh3

© www.soinside.com 2019 - 2024. All rights reserved.