简体   繁体   English

如何在C ++中从枚举转换为类型(并在模板中使用)?

[英]How to do a conversion from enum to type (and use as it in a template) in C++?

This is something that is possible in C# (see How to convert enum to type ), but I wonder how I should implement it in C++11 ? 这是C#中可能的(参见如何将枚举转换为类型 ),但我想知道如何在C ++ 11中实现它?

A possible code skeleton (which also shows my intended use) would be : 可能的代码框架(也显示我的预期用途)将是:

// classify.h (part of public API) 
// definitions of classes A, B, C cannot be exposed, only a list of names
// (some names might even refer to the same type of object)

enum EAllowedTypes { eA, eB, eC };
class Entity;

bool classifyEntityAs(const Entity& ent, EAllowedTypes type);

_ _

// classify.cpp
#include "A.h"  // defines class A (derived from a common base class)
#include "BC.h" // defines class BC (derived from a common base class)

template <typename T> 
bool classify(const Entity &) 
{ 
    /* some implementation, with possible specializations */
}

// how to implement this kind of vector/map from enum(int) to existing classes
static const std::vector enumToType</*ClassType*/> =
{
    /* eA */ class A,    
    /* eB */ class BC,
    /* eC */ class BC
};

bool classifyEntityAs(cont Entity& ent, EAllowedTypes type)
{
    return classify<enumToType[type]>(ent);
}

Some clarification based on the comments: 1) classify.h is part of a public API (for third parties/other companies), hence, type cannot be known at compile time. 基于注释的一些澄清:1)classify.h是公共API的一部分(对于第三方/其他公司),因此,在编译时无法识别类型。 Yet there is no problem to compile the template for all types in EAllowedTypes. 然而,为EAllowedTypes中的所有类型编译模板没有问题。 The EAllowedTypes is there mainly for communication purposes with the client and to allow future changes to the internal data types (without having to change the public API), so there is no guarantee to a one-on-one mapping. EAllowedTypes主要用于与客户端进行通信,并允许将来更改内部数据类型(无需更改公共API),因此无法保证一对一映射。 2) Entity is not a base class of any of A, B, BC, etc. It is just a generic Object with which we want to associate a classification A, B or C. The implementation details of how A, B, etc. is attached should remain hidden. 2)实体不是A,B,BC等中任何一个的基类。它只是一个通用的对象,我们想要将它与A,B或C分类相关联.A,B等的实现细节。附加应该保持隐藏。

You could do the following: 您可以执行以下操作:

template <EAllowedTypes e, typename T>
struct cl {
    static const decltype(e) value = e;
    using type = T;
};

template <typename...>
struct classifier;

template <>
struct classifier<> {
    static bool classify(const Entity& ent, EAllowedTypes type) {
        throw std::logic_error("Whatever... ");
    }
};

template <typename T, typename... Args>
struct classifier<T, Args...> {
    static bool classify(const Entity& ent, EAllowedTypes type) {
        return type == T::value ? 
            ::classify<typename T::type>(ent)
            : classifier<Args...>::classify(ent, type);
    }
};

bool classifyEntityAs(const Entity& ent, EAllowedTypes type) {
    return classifier<cl<eA, A>, cl<eB, BC>, cl<eC, BC>>::classify(ent, type);
}

Which would require you to add a new cl<eX, X> each time you want to add a new possible class. 每次要添加新的可能类时cl<eX, X>都需要添加新的cl<eX, X> This is basically a "recursive" switch , that will likely be optimized as a simple switch 1 by the compiler. 这基本上是一个“递归” switch ,可能会被编译器优化为简单的switch 1 The idea here is to link the enum values with their classes using the cl structures. 这里的想法是使用cl结构将enum值与其类链接起来。

If you want to store the list of cl outside, you could do something like: 如果你想在外面存储cl列表,你可以这样做:

using enumToType = std::tuple<cl<eA, A>, cl<eB, BC>, cl<eC, BC>>;

template <size_t... Idx>
bool classifyEntityAs(const Entity& ent, EAllowedTypes type, 
                      std::index_sequence<Idx...>) {
    return classifier<std::tuple_element_t<Idx, enumToType>...>::classify(ent, type);
}

bool classifyEntityAs(const Entity& ent, EAllowedTypes type) {
    return classifyEntityAs(ent, type, 
        std::make_index_sequence<std::tuple_size<enumToType>{}>{});
}

And then you would only have to modify the definition of enumToType (I am using std::tuple here, but you could have a custom structure that only holds types). 然后你只需要修改enumToType的定义(我在这里使用std::tuple ,但你可以拥有一个只保存类型的自定义结构)。


1 This code is basically equivalent to: 1此代码基本等同于:

switch (type) {
  case eA: return classify<A>(ent);
  case eB: return classify<BC>(ent);
  case eC: return classify<BC>(ent);
  default: throw std::logic_error("Whatever... ");
}

There is no way to do it directly as you desired as c++ does not support reflection mechanism, and I don't think it will change at least for a while... But... if you know which types should be supported by your classifier you could adjust following code to your use case scenario: 有没有办法,你需要的是C ++ 支持反射机制直接做到这一点,我不认为它会改变至少一会儿......但是......如果你知道哪些类型应该由你的支持您可以根据代码调整分类器到您的用例场景:

#include <map>
#include <memory>
#include <iostream>


enum EAllowedTypes { eA, eB, eC };

struct A {
  void run() {
    std::cout << "A::run() invoked" << std::endl;
  }
};
struct B {
  void run() {
    std::cout << "B::run() invoked" << std::endl;
  }
};
struct C {
  void run() {
    std::cout << "C::run() invoked" << std::endl;
  }
};

struct ITypeWrapper {
   virtual void run() = 0;
};

template <class T>
struct TypeWrapper: ITypeWrapper {
   void run() {
      T t;
      t.run();
   }
};

template <EAllowedTypes AT, class Type>
struct Mapper { };

template <class... Mappers>
struct Classifier;

template <EAllowedTypes... ATs, class... Types>
struct Classifier<Mapper<ATs, Types>...> {
   static std::map<EAllowedTypes, std::shared_ptr<ITypeWrapper>> m;
};

template <EAllowedTypes... ATs, class... Types>
std::map<EAllowedTypes, std::shared_ptr<ITypeWrapper>> Classifier<Mapper<ATs, Types>...>::m = { { ATs, std::make_shared<TypeWrapper<Types>>() }... };

using ABCClassifier = Classifier<Mapper<eA, A>, Mapper<eB, B>, Mapper<eC, C>>;

int main() {
   ABCClassifier::m[eA]->run();
}

Output: 输出:

A::run() invoked 调用了一个:: run()

Just drop the enum. 只需删掉枚举。 Enums represent number. 枚举代​​表数字。 Types are types. 类型是类型。 Why mix the two? 为什么混合这两个? You can do the same only with types and templates! 您只能对类型和模板执行相同的操作!

First, let's change your enum of allowed type to a list of type, which probably suits your problem much better: 首先,让我们将允许类型的枚举更改为类型列表,这可能更适合您的问题:

using AllowedTypes = std::tuple<TypeA, TypeB, TypeC>;

After that, instead of sending your function classifyEntityAs a number, let's send it a type: 之后,不要将函数classifyEntityAs作为数字发送,而是发送一个类型:

template<typename T>
bool classifyEntityAs(const Entity& ent) { /* ... */ }

Now, sending the type to the classify function is pretty easy: 现在,将类型发送到classify函数非常简单:

template<typename T>
bool classifyEntityAs(const Entity& ent) {
    classify<T>(ent);
}

If you want to restrict the type you can send to the function, we will need some meta programming , which will consist of comparing types to check if a type T is one of the type list. 如果你想限制你可以发送给函数的类型,我们将需要一些元编程 ,它将包括比较类型以检查类型T是否是类型列表之一。

template<typename, typename>
struct is_one_of_list;

// Case where the list is empty. T cannot match to an empty list
template<typename T>
struct is_one_of_list<T, std::tuple<>> : std::false_type {};

// Case where the list is at least one.
// We match the type T with Head.
// If T is not the same as Head, match with Tail list using is_one_of_list
template<typename T, typename Head, typename... Tail>
struct is_one_of_list<T, std::tuple<Head, Tail...>> : std::integral_constant<bool,
    std::is_same<T, Head>::value || is_one_of_list<T, std::tuple<Tail...>>::value
> {};

Okay, now the hard part is done. 好的,现在困难的部分已经完成。 We can restrict your function with the new type trait we made: 我们可以使用我们制作的新类型特征限制您的功能:

template<typename T, std::enable_if_t<is_one_of_list<T, AllowedTypes>::value>* = nullptr>
bool classifyEntityAs(const Entity& ent) { /* ... */ }

When calling your function, only types from AllowedTypes will be allowed. 调用函数时,只允许使用AllowedTypes类型。

You can check a live example on Coliru 您可以查看Coliru上的实例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM