根据枚举值设置类型(在结构中)等于其他结构的成员类型

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

用例:根据“源”中的枚举值获取属性并在结构

Attributes
中设置值。如果无法检索值,则返回
ErrorCode
:

enum class ErrorCode
{
    OK,
    FAILURE
};
enum AttributeType
{
    TYPE1,
    TYPE2,
    TYPE3
};
struct Attributes
{
    uint16_t attribute1;
    uint8_t attribute2;
    bool attribute3;
};

建议的解决方案:使用工厂,其中基类定义接口方法

get
,派生类(每个属性类型)实现该方法。整个
Attributes
对象需要传递给该方法,并且在一个实现中仅设置结构体的一个成员(基于属性类型)。

class AttributeRetriever
{
public:
    virtual ErrorCode get(Attributes& attributes) = 0;
};
class Attributetype1 : public AttributeRetriever
{
public:
    ErrorCode get(Attributes& attributes) override
    {
        attributes.attribute1 = 60;
        return ErrorCode::OK;
    }
};
std::unique_ptr<AttributeRetriever> factory(AttributeType at)
{
    if (at == TYPE1) return std::make_unique<Attributetype1>();
    ...
}

我的解决方案:使用模板。我们没有将

Attributes
传递给方法,而是从中返回所需的值。由于我们需要返回结果值或错误,因此我创建了一个类
Result
。我的解决方案创建一个模板结构
TypeMap
,以枚举值作为参数,其中
type
等于
Attributes
中的属性类型(基于枚举值):

template <typename ValueType>
class Result
{
public:
    static_assert(std::is_copy_constructible<ValueType>::value && std::is_copy_assignable<ValueType>::value, "ValueType must be both copy-constructible and copy-assignable");
    static_assert(std::is_move_constructible<ValueType>::value && std::is_move_assignable<ValueType>::value, "ValueType must be both move-constructible and move-assignable");

    explicit Result(ErrorCode e) noexcept : m_value{}, m_error{e}, m_has_error{true}  {}

    explicit Result(ValueType t) noexcept : m_value{t}, m_error{ErrorCode::OK}, m_has_error{false}  {}

    [[nodiscard]] bool has_value() const noexcept { return !m_has_error; }

    [[nodiscard]] bool has_error() const noexcept { return m_has_error; }

    [[nodiscard]] ValueType value() const noexcept { return m_value; }

    [[nodiscard]] ErrorCode error() const noexcept { return m_error; }

private:
    const ValueType m_value;
    const ErrorCode m_error;
    const bool m_has_error;
};

template<AttributeType> struct TypeMap;

template<>
struct TypeMap<AttributeType::TYPE1>
{
    using type = decltype(Attributes::attribute1);
};

template<>
struct TypeMap<AttributeType::TYPE2>
{
    using type = decltype(Attributes::attribute2);
};

template<>
struct TypeMap<AttributeType::TYPE3>
{
    using type = decltype(Attributes::attribute3);
};

class AttributeRetriever
{
public:
    template<AttributeType AT>
    Result<typename TypeMap<AT>::type> get()
    {
        using R = Result<typename TypeMap<AT>::type>;

        if constexpr(AT == AttributeType::TYPE1)
        {
            return R(60);
        }
        else if (AT == AttributeType::TYPE2)
        {
            return R(3);
        }
        else if (AT == AttributeType::TYPE3)
        {
            return R(false);
        }
    }
};
int main()
{
    AttributeRetriever ar;
    std::cout << ar.get<AttributeType::TYPE1>().value() << '\n';
    std::cout << static_cast<uint32_t>(ar.get<AttributeType::TYPE2>().value()) << '\n';
    std::cout << ar.get<AttributeType::TYPE3>().value() << '\n';
}

我的解决方案有效。但是

TypeMap
需要针对每个属性类型和结构成员进行专门化。有没有更优雅的解决方案
TypeMap

c++ templates struct enums
2个回答
1
投票

一种选择是将类型映射缩短为:

template<AE i>
using TypeMap = typename std::tuple_element<i, std::tuple<
  decltype(A::a),
  decltype(A::b)
>>::type;

1
投票

C++ 不支持这种反射:您无法轻松访问声明结构体字段的顺序。除了构造函数中的初始化顺序或手动分析偏移量之外,以下结构

struct Attributes
{
  uint8_t attribute2;
  uint16_t attribute1;
  bool attribute3;
};

将相当于您的

Attributes
。 现有的迂回解决方案如“magic get”,但通常有两种方法可以做类似的事情。

a) 指向成员的指针。您可以将它们用作模板参数:

template<auto m> struct field_helper;
template<typename T, typename FieldType, FieldType T::*memberPtr>
struct field_helper<memberPtr> {
    static FieldType get(const T* ptr){ return ptr->*memberPtr; }
};

在这种情况下,您的枚举完全消失:它与字段名称合并,删除重复项。如果您不需要代码来迭代每个结构体字段,我建议尝试走这条路线。

b) 宏。您可以使用为每个类成员创建结构专业化的宏来声明字段:

#define DECLARE_FIELD(IDX, Type, name) Type name; \
  template<typename C> struct field_helper<IDX, C> { \
    static Type get(MainType* ptr) { return ptr->name; } \
  }

struct Atrributes {
    //need typedef since you can't get type name within the type
    typedef Attributes MainType;
    //need extra template parameter to prevent full specialization
    template<int idx, typename = void> struct field_helper;
    DECLARE_FIELD(0, uint16_t, attribute1);
    //...
};

有了这个,所有字段都被明确编号:您可以对它们进行计数,迭代它们,通过索引访问它们的类型等。(您可以通过更多宏观工作自动编号,但这超出了您的问题的范围。)

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