简体   繁体   中英

Map enum value to a type in C++

Is there any way to map enum values to types in C++, including C++11.
I have the following enum type:

enum ATTRIBUTE{AGE=0, MENOPAUSE, TUMOR_SIZE, INV_NODES, NODE_CAPS,
               DEG_MALIG, BREAST, BREAST_QUAD, IRRADIAT, CLASS};

I want to map each value of this enum to a certain type. I want to map AGE to int , MENOPAUSE to another enum type, BREAST to bool and so on.
So is it possible to create a function which returns a value of type which depends on the value of the attr variable?

//Like that:
auto value = map_attr(ATTRIBUTE attr);
//Here the type of the value variable should be int if the attr variable is AGE, bool for BREAST and so on.

An idiomatic way to do it is using traits:

enum ATTRIBUTE{ AGE=0, MENOPAUSE, TUMOR_SIZE, INV_NODES, NODE_CAPS, DEG_MALIG, BREAST, BREAST_QUAD, IRRADIAT, CLASS };

template<ATTRIBUTE> struct Map;

template<> struct Map<AGE> {
    using type = int;
    static constexpr type value = 42;
};

template<> struct Map<MENOPAUSE> {
    using type = AnotherEnumType;
    static constexpr type value = AnotherEnumType::AnotherEnumValue;
};

// ...

Then you can define map_attr as a function template:

template<ATTRIBUTE A>
typename Map<A>::type map_attr() { return Map<A>::value; }

And use it as:

auto something = map_attr<AGE>();

It follows a minimal, working example:

#include<type_traits>

enum ATTRIBUTE{ AGE=0, MENOPAUSE };

template<ATTRIBUTE> struct Map;

template<> struct Map<AGE> {
    using type = int;
    static constexpr type value = 42;
};

template<> struct Map<MENOPAUSE> {
    using type = double;
    static constexpr type value = 0.;
};

template<ATTRIBUTE A>
typename Map<A>::type map_attr() { return Map<A>::value; }

int main() {
    static_assert(std::is_same<decltype(map_attr<AGE>()), int>::value, "!");
    static_assert(std::is_same<decltype(map_attr<MENOPAUSE>()), double>::value, "!");
}

I tried out the approach in skypjack's answer using a vector<void*> , to gain a better understanding of it:

#include <iostream>
#include <vector>

using namespace std;

enum Types
{
    Int,
    Double
};

//Use struct instead of class for auto-public.
template<Types> struct TypesMap;

template<> struct TypesMap<Int>
{
    using type = int;
};

template<> struct TypesMap<Double>
{
    using type = double;
};

int main() {
    std::vector<void*> Test;
    
    //Note: Memory leak here, but just as an example.
    Test.push_back(new TypesMap<Int>::type(0.5));
    Test.push_back(new TypesMap<Double>::type(0.5));
    
    std::cout << *((TypesMap<Int>::type*)Test[0]) << " " << *((TypesMap<Double>::type*)Test[1]);
    
    return 0;
}

Output: 0 0.5

Try it here: https://ideone.com/0bN1IP

The problem I've discovered with it, however, is that you cannot put runtime-only enum values (eg if you had a vector<Types> ) in the <> when you go to use it. This severely limits its capabilities.

My understanding is that this limitation is due to templating existing only for compile-time resolution.


The inherent problem here is wanting dynamic typing whilest c++ is statically typed. So, unless you implement some kind of big switch statement/function to handle each type individually, a polymorphic class, or some other manual type conversion code, it is not possible to have dynamic typing like this resolved at runtime, only that which can be resolved at compile-time. (These other solutions mentioned may be suitable for you if you're okay with somewhat slower code.)

I wrote a header-only lib called magic_params lib to solve exactly this problem, check it out here .

Basically you define your mapping from enum to type and default value once and can call it's default value at compile time and set and get different values for every enum at runtime.

Example: given an enum like

enum class Param
{
   one = 0,
   two,
   three
};

you can define your mapping with

static constexpr auto settings = create(
       Add<Param::one, int>(99,Description{"first parameter"}), 
       Add<Param::two, std::string>("Default-String", Description("second parameter")),
       Add<Param::three, bool>(true, Description{"third parameter"}));

and call it like that:

constexpr auto value = MyMagicParams::getDefault<Param::two>();

std::cout << "Parameter 'two' has default value " << value;

auto myParams = MyMagicParams();
auto valueBefore = myParams.get<Param::one>();
myParams.set<Param::one>(3);
auto valueAfter = myParams.get<Param::one>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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