简体   繁体   English

在C++中,枚举值和数据类型之间的map如何才能在模板中使用这些类型?

[英]In C++, how can one map between enum values and data types, so the types can be used in templates?

How can one do this, which is obviously impossible C++, in real C++?:如何做到这一点,这显然是不可能的 C++,在真实的 C++ 中?:

Type decodeUiEnum(UiEnum myEnum) { // impossible: cannot return a data type
     // one switch statement to rule them all
     switch(myEnum) {
          case USER_SELECTED_GREYSCALE: return GreyscalePixel;
          case USER_SELECTED_RGB: return RgbPixel;
          ...
     }
}

void doSomeGraphicsMagic1(UiEnum myEnum) {
     ...
     Foo<decodeUiEnum(myEnum)> a(...); // impossible: type not available at compile 
                                       // time
     ...
}

void doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
     ...
     Bar<int, decodeUiEnum(myEnum)> b(...); // impossible
     ...
}

and the like, so you can just add new types to the top switch statement and not have to modify the other code below it, so long as that code is suitably generic of course?等等,所以您可以只向顶部的 switch 语句添加新类型,而不必修改它下面的其他代码,当然只要该代码是适当的通用代码就可以了? As otherwise, you would need a switch statement within each function to do the necessary type mapping into the templates, which is not as much maintainable code, and lots of duplication.否则,您需要在每个 function 中使用一个 switch 语句来将必要的类型映射到模板中,这不是那么多可维护的代码,而且有很多重复。 So more generally - if this is approaching it the wrong way, how do we fulfill that intended property of the code?所以更一般地说 - 如果这是以错误的方式接近它,我们如何实现代码的预期属性?

That is, what I want to do is, in a function taking an enum as parameter, instantiate a template type where the template parameter depends on the enum, without having a switch-on-enum in every function.也就是说,我想要做的是,在一个以枚举作为参数的 function 中,实例化一个模板类型,其中模板参数取决于枚举,而不是在每个 function 中都有一个 switch-on-enum。

Yes it is actually possible.是的,这实际上是可能的。

Trick is based on partial template specification, this approach used by std::get技巧基于部分模板规范, std::get使用的这种方法

For example:例如:

    #include <iostream>
    // specify an enumeration we will use as type index and related data types
    enum class UiEnum {
        GRAY_SCALE,
        RGB_PIXEL
    };
    
    struct GreyscalePixel;
    struct RgbPixel;
    
    // make base template class
    template<UiEnum _EV>
    struct ui_enum_type {
    };
    
    // do partial type specification trick
    // insert typedefs with data type we need for each enumeration value
    template<>
    struct ui_enum_type<UiEnum::GRAY_SCALE> {
        typedef GreyscalePixel pixel_type;
    };
    
    template<>
    struct ui_enum_type<UiEnum::RGB_PIXEL> {
        typedef RgbPixel pixel_type;
    };
    
    
    // demo classes to demonstrate how trick is working at runtime
    
    template<typename T>
    struct demo_class {
    };
    
    template <>
    struct demo_class<GreyscalePixel> {
        demo_class()
        {
            std::cout << "GreyscalePixel" << std::endl;
        }
    };
    template <>
    struct demo_class<RgbPixel> {
        demo_class()
        {
            std::cout << "RgbPixel" << std::endl;
        }
    };
    
    // use swithc trick
    static void swich_trick(std::size_t runtimeValue)
    {
        switch( static_cast<UiEnum>(runtimeValue) ) {
        case UiEnum::GRAY_SCALE: {
            demo_class< ui_enum_type<UiEnum::GRAY_SCALE>::pixel_type > demo1;
        }
        break;
        case UiEnum::RGB_PIXEL: {
            demo_class< ui_enum_type<UiEnum::RGB_PIXEL>::pixel_type > demo2;
        }
        break;
        }
    }
    
    int main(int argc, const char** argv)
    {
        // Do runtime based on the trick, use enum instead of data type
        for(std::size_t i=0; i < 2; i++) {
            swich_trick(i);
        }
        return 0;
    } 

In any case my suggestion - use classic polymorphism instead of template meta-programming over complication.无论如何,我的建议是使用经典的多态性而不是复杂化的模板元编程。 Most modern compilers doing de-virtualization during optimization.大多数现代编译器在优化期间进行去虚拟化。 For example:例如:

#include <iostream>
#include <memory>
#include <unordered_map>

enum class UiEnum {
    GRAY_SCALE,
    RGB_PIXEL
};

class GraphicsMagic {
    GraphicsMagic(const GraphicsMagic&) = delete;
    GraphicsMagic& operator=(const GraphicsMagic&) = delete;
protected:
    GraphicsMagic() = default;
public:
    virtual ~GraphicsMagic( ) = default;
    virtual void doSome() = 0;
};

class GreyscaleGraphicsMagic final: public GraphicsMagic {
public:
    GreyscaleGraphicsMagic():
        GraphicsMagic()
    {
    }

    virtual void doSome() override
    {
        std::cout << "GreyscalePixel" << std::endl;
    }
};

class RgbGraphicsMagic final: public GraphicsMagic {
public:
    RgbGraphicsMagic():
        GraphicsMagic()
    {
    }
    virtual void doSome() override
    {
        std::cout << "RgbPixel" << std::endl;
    }
};

int main(int argc, const char** argv)
{
    std::unordered_map< UiEnum, std::shared_ptr< GraphicsMagic > > handlers;
    handlers.emplace(UiEnum::GRAY_SCALE, new GreyscaleGraphicsMagic() ) ;
    handlers.emplace(UiEnum::RGB_PIXEL, new RgbGraphicsMagic() );

    for(std::size_t i=0; i < 2; i++) {
        handlers.at( static_cast<UiEnum>(i) )->doSome();
    }

    return 0;
}

You could use std::variant , and then have consuming code std::visit that variant.您可以使用std::variant ,然后使用代码std::visit该变体。

First we want a template for "pass a type as a parameter"首先,我们想要一个“将类型作为参数传递”的模板

template <typename T>
struct tag {
    using type = T;
};

Then we define our variant and the factory for it.然后我们为它定义我们的变体和工厂。

using PixelType = std::variant<tag<GreyscalePixel>, tag<RgbPixel>>;

PixelType decodeUiEnum(UiEnum myEnum) {
    switch(myEnum) {
        case USER_SELECTED_GREYSCALE: return tag<GreyscalePixel>{};
        case USER_SELECTED_RGB: return tag<RgbPixel>{};
        ...
    }
}

Now our methods can be written as visitors over PixelType现在我们的方法可以写成PixelType的访问者

void doSomeGraphicsMagic1(UiEnum myEnum) {
     std::visit([](auto t){
         using Pixel = decltype(t)::type;
         Foo<Pixel> a(...);
     }, decodeUiEnum(myEnum));
}

int doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
     return std::visit([blah](auto t){
         using Pixel = decltype(t)::type;
         Bar<int, Pixel> a(...);
         return a.frob();
     }, decodeUiEnum(myEnum));
}

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

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