tzippy
tzippy

Reputation: 6638

Getting all values from an enum

I have classes in the style of Class1 (see code). An enum and a function to get all the values from the enum. The values (FOO_1, FOO_2 etc) differ from Class to Class as well as the number of values (sizeof(Foos)). I call the function once to get the sizeof the enum, reserve memory and with the second call I want to get all the values to *pFoos (2,1,6 in the sample code). Is there a better way then using an array with all the values in it (size_t arr[3] ={FOO_1 , FOO_X, FOO_BAR })?

class Class1{
    enum Foos{
        FOO_1 = 2,
        FOO_X = 1,
        FOO_BAR = 6
    }
};

Class1::GetFoos(size_t* pFoos, size_t* pSize)
{
    size_t len = sizeof(Foos);
    if (len > *pSize)
    {   //Call function once to get the size
        *pSize= len ;
        return -1;
    }
    for(size_t i = 0; i< *pSize; i++)
    {
       //copy all enum values to pFoos
    }
};

Upvotes: 6

Views: 11985

Answers (4)

Vrtulka23
Vrtulka23

Reputation: 31

Using macros can be quite useful in this matter. Below is an example of a simple solution how to generate list of value names along with the Enums. And it does not require any module loading as in the antron's answer.

#include <iostream>
#include <string>

#define FOREACH_COLOR(COLOR) \
    COLOR(Red)   \
    COLOR(Green) \
    COLOR(Blue)  \

// Define the enum
enum class Color {
    #define GENERATE_ENUM(ENUM) ENUM,
    FOREACH_COLOR(GENERATE_ENUM)
    #undef GENERATE_ENUM
};

// Array of enum names
const char* ColorNames[] = {
    #define GENERATE_STRING(STRING) #STRING,
    FOREACH_COLOR(GENERATE_STRING)
    #undef GENERATE_STRING
};

int main() {
    // Iterate over all enum values and their corresponding names
    for (int i = 0; i < 3; ++i) {
        std::cout << "Key: " << i << " Value: " << ColorNames[i] << std::endl;
    }
    std::cout << "Red: " << (int)Color::Red << std::endl;
    std::cout << "Green: " << (int)Color::Green << std::endl;
    std::cout << "Blue: " << (int)Color::Blue << std::endl;    
    return 0;
}

The above code should generate something like:

Key: 0 Value: Red
Key: 1 Value: Green
Key: 2 Value: Blue
Red: 0
Green: 1
Blue: 2

Upvotes: 0

antron
antron

Reputation: 3847

Disclaimer: shameless plug – I am the author.

Reflective enums are possible in C++. I wrote a header-only library that captures a bunch of "patterns" at compile time and gives you syntax like this:

ENUM(Class1, int, FOO_1 = 2, FOO_X = 1, FOO_BAR = 6)

size_t count = Class1::_size;

for (size_t index = 0; index < Class1::_size; ++index)
    do_anything(Class1::_values()[index]);

What it does internally is use the macro to generate an array of the values that you have declared, kind of like in your question, and use a bunch of other tricks to allow you to use initializers naturally. It then provides iterators and other things on top of the array.

Here is a link: https://github.com/aantron/better-enums

EDIT – internals

Here is a pseudocode sketch of what it does internally. The reason I am only giving a "sketch" is because there are a bunch of issues to consider when doing this portably. I will touch on all the most important elements.

ENUM(Class1, int, FOO_1 = 2, FOO_X = 1, FOO_BAR = 6)

notionally expands to

struct Class1 {
    enum _enumerated { FOO_1 = 2, FOO_X = 1, FOO_BAR = 6 };

    // Fairly obvious methods for how to iterate over _values and
    // _names go here. Iterators are simply pointers into _values
    // and _names below.

    static size_t _size = sizeof(_values) / sizeof(int);

    int _value;
};

int _values[] = {(fix_t<Class1>)Class1::FOO_1 = 2,
                 (fix_t<Class1>)Class1::FOO_X = 1,
                 (fix_t<Class1>)Class1::FOO_BAR = 6};
const char *_names[] = {"FOO_1 = 2", "FOO_X = 1", "FOO_BAR = 6"};

This is done by using variadic macros and stringization. The methods that deal with strings treat not only \0, but also space and equals as terminators, which allows them to ignore the initializers in the stringized constants that you see in _names.

The type fix_t is necessary because having assignments inside an array initializer is not valid C++. What that type does is take on the value of the enum, then ignore the assignment by an overloaded assignment operator, and then return the original value. A sketch:

template <typename Enum>
struct fix_t {
    Enum _value;

    fix_t(Enum value) : _value(value) { }
    const fix_t& operator =(int anything) const { return *this; }
    operator Enum() const { return _value; }
};

This makes the _values array possible declare even in the presence of initializers.

Of course, these arrays need to be prefixed so that you can have more than one enum like this. They also need to have the same as "extern inline" linkage for functions, so that they are shared between multiple compilation units.

Upvotes: 6

PaperBirdMaster
PaperBirdMaster

Reputation: 13320

Is there a better way then using an array with all the values in it (size_t arr[3] ={FOO_1 , FOO_X, FOO_BAR })?

If you're tagging the question as C++ I advise you to give up with the C way of doing things, so the better way to do this in C++ is using a std::vector:

class Class1{
    enum Foos{
        FOO_1 = 2,
        FOO_X = 1,
        FOO_BAR = 6
    };
public:
    std::vector<int> GetFoos()
    {
        // return all enum values
        return {FOO_1, FOO_X, FOO_BAR};
    }
};

You can use it this way:

Class1 c1;
auto foos = c1.GetFoos();
std::cout << "I have " << c1.size() << " foos:\n";
for (const auto &foo : foos) std::cout << foo << '\n';

If you don't want to create the vector at runtime, you can create it once declaring it static:

class Alpha{
    enum Alphas{
        BETA = 0b101010,
        GAMMA = 0x20,
        EPSILON = 050
    };
    static const std::vector<int> m_alphas;
public:
    const std::vector<int> &GetAlphas()
    {
        return m_alphas;
    }
};

// https://isocpp.org/wiki/faq/ctors#explicit-define-static-data-mems
const std::vector<int> Alpha::m_alphas = {BETA, GAMMA, EPSILON};

Live demo

I know that is a burden to maintain but since there's no way to iterate the values of an enum, all the code that tries to iterate them is a burden as well.

Maybe in the following answer you can find something useful to iterate enums in a better way for your goals:

Upvotes: 1

Klaus
Klaus

Reputation: 25613

Until c++ will get reflection you will not get any data from your enum! Simply you can not get "all" values from an enum. A enum is simply a kind of namespace where some constants can be defined and may be enumerated automatically. Not more at all. You have no text representation, no count information, no value to text information!

Upvotes: 4

Related Questions