Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 Automate creation of integer to enum value map

I need to create a map of int to enum values. (I'm reading integers in a file and need to create an enum from them at runtime.) I can manually create a map as shown in my example below. However this is a trivial example with only a few (i.e., seven) elements of my enum.

My real-world-problem has several hundred elements of my enum class. I don't need to print the names of my real-world enum, but I do need to get the enum value given an integer. I already have the enum class created and want an automatic way to create a map from an integer to the enum value.

I'm looking to automate the creation of the map which I've called WeekMap so I can pass it an integer and get the enum value. Is this even possible? Please tell me it is.

// Compile with:
//      clang++ -std=c++11 -stdlib=libc++ enum_test.cpp -o enum_test
// 

#include <iostream>
#include <string>
#include <map>

enum class Days {
    SUNDAY    = 1,
    MONDAY    = 2,
    TUESDAY   = 3,
    WEDNESDAY = 4,
    THURSDAY  = 5,
    FRIDAY    = 6,
    SATURDAY  = 7
};

std::ostream& operator<< (std::ostream& os, const Days& day){
    os << static_cast<std::underlying_type<Days>::type>(day);
    return os;
}

std::map<unsigned int, Days> WeekMap{
    {1, Days::SUNDAY},
    {2, Days::MONDAY},
    {3, Days::TUESDAY},
    {4, Days::WEDNESDAY},
    {5, Days::THURSDAY},
    {6, Days::FRIDAY},
    {7, Days::SATURDAY},
};

// Return the day of the week
Days WhatDay(unsigned int D){
    return WeekMap[D];
}

int main() {

    std::cout << "\nLearning how to 'cout' enums." << std::endl;

    Days myDay = Days::MONDAY;

    std::cout << "\nMonday is day: " << myDay << "\n" << std::endl;

    for (int i=1; i<8; i++){
        std::cout << "Day number: " << i << " is " << WhatDay(i) << std::endl;
    }

    return 0;
}
like image 601
jlconlin Avatar asked Feb 03 '26 05:02

jlconlin


1 Answers

You do not need the map. Your WhatDay function can be written like this:

Days WhatDay (unsigned int D) {
    return static_cast<Days>(D);
}

This is basically free (in efficiency terms.) But you might want to make sure your enum's underlying type is indeed an int (or something smaller or larger;) again for efficiency reasons and for reliability reasons:

enum class Days : int {
    ...
};

What you will lose, however, with this method is error checking. You won't be able to check whether the integer is a valid enumeration value or not; especially if your enum values are not contiguous.

UPDATE 2 (Update 1 is below!) To somewhat automate creation of this kind of enum, and a whole lot of other code, you can use the following technique:

You first write down the data you are interested in, in a general format:

#define EXPAND_VALUES(action)           \
    action (1, SUNDAY,    "Sunday")     \
    action (2, MONDAY,    "Monday")     \
    action (3, TUESDAY,   "Tuesday")    \
    action (4, WEDNESDAY, "Wednesday")  \
    action (5, THURSDAY,  "Thursday")   \
    action (6, FRIDAY,    "Friday")     \
    action (7, SATURDAY,  "Saturday")
    // Note the lack of a separator after the entries; this is more flexible.

This is all the information I have about each of the entries, in a general form (i.e. passed to an unknown function-like thing called action.

Now, to define the enumeration, we can simply say:

#define DEF_ENUM(i,v,s) v = i,
enum class Days : int { EXPAND_VALUES(DEF_ENUM) };
#undef DEF_ENUM

As further examples, you can define the map you want, and another table that maps enum values to strings like this:

#define DEF_MAP(i,v,s)  {i, Days::v},
std::map<int, Days> WeekMap { EXPAND_VALUES(DEF_MAP) };
#undef DEF_MAP

#define DEF_STR_MAP(i,v,s)  {Days::v, s},
std::map<Days, std::string> WeekStrMap { EXPAND_VALUES(DEF_STR_MAP) };
#undef DEF_STR_MAP

(This sample code is available on Ideone.)

Note what this technique does for you. Without any redundancy in data definition, you get as many data-structure definitions, array initializations, switch-statement cases, if-else if-else constructs, etc. you want out of that data. And all of this is done at compile time (or before) without any hassles.

This is an extremely powerful technique that might (or might not) be useful to you.


UPDATE 1 (In response to the updated question):

No, it is not possible to create an enum at runtime in C++. You cannot create a new type for the compiler at runtime, when there is no more compiler (if this is really what you're after.)

However, since enums in C++ offer absolutely no runtime facilities, then, at runtime, there is no difference between two enums with the same underlying type. Or in fact, between an enum and an int (if int is the underlying type for that enum.)

Therefore, I propose this: you define an empty enum, and write the WhatDay function exactly as before. And everything will be fine! (Since you don't need bounds checking.)

Specifically, what I'm proposing is this:

enum class Days : int { /*really, really empty!*/ };

Days WhatDay (unsigned int D) {
    return static_cast<Days>(D);
}

This works because it seems to me that you do not know your enumeration values at compile time, and the compiler doesn't care one bit for the values of an enum at runtime. No one does!

And if you want error and range checking, I suggest you use an "Interval Tree" (read up on it on Wikipedia.) You populate this tree when you read your enum values from your file at runtime (and this is the only data structure you populate,) and then check each value passed to the WhatDay function against it.

like image 130
yzt Avatar answered Feb 04 '26 19:02

yzt