从枚举中获取所有的值

6
我有一些类似于Class1风格的课程(见代码)。一个枚举和一个函数用来获取枚举中的所有值。这些值(如FOO_1FOO_2等)从一个类到另一个类都不同,值的数量也不同(如sizeof(Foos))。 我调用该函数一次以获取枚举的大小,预留内存,并在第二次调用中想要获取所有值到*pFoos中(示例代码中为216)。 除了使用包含所有值的数组(如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
    }
};

2
可能是重复问题:如何在枚举中进行迭代? - Basilevs
您似乎试图将枚举误用为容器,但它们并不是容器。 - Lightness Races in Orbit
3个回答

6

免责声明:我是这篇文章的作者。

C++ 中可以使用反射枚举。我写了一个只有头文件的库,可以在编译时捕获一堆“模式”,并给你提供下面这样的语法:

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]);

它在内部使用宏来生成您声明的值的数组,有点像您的问题,并使用其他技巧使您能够自然地使用初始化程序。然后,在数组的基础上提供了迭代器和其他东西。这是一个链接:https://github.com/aantron/better-enums 编辑-内部:
以下是伪代码概述其内部操作。之所以只给出“概述”,是因为在进行此类操作时需要考虑一些问题。我将涵盖所有最重要的要素。
ENUM(Class1, int, FOO_1 = 2, FOO_X = 1, FOO_BAR = 6)

概念上扩展到。
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"};

这是通过使用可变参数宏和字符串化实现的。处理字符串的方法不仅处理\0,还处理空格和等号作为终止符,这使得它们可以忽略在_names中看到的字符串常量中的初始化器。
类型fix_t是必需的,因为在数组初始化程序中进行赋值是无效的C++。该类型的作用是获取枚举的值,然后通过重载的赋值运算符忽略赋值,最后返回原始值。一个草图:
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; }
};

这使得_values数组即使在存在初始化器的情况下也是可能声明的。
当然,这些数组需要添加前缀,以便您可以拥有多个类似的枚举。它们还需要与函数具有相同的“extern inline”链接,以便它们可以在多个编译单元之间共享。

4

在C++没有反射之前,您将无法从枚举中获取任何数据!简单地说,您无法从枚举中获取“所有”值。枚举只是一种命名空间,在其中可以定义一些常量,并可以自动枚举。仅此而已。您没有文本表示形式,没有计数信息,也没有值与文本信息!


枚举类型不仅仅是一个命名空间,还有更多的内容。以这段代码为例,你可以看到当我尝试将1传递给枚举类型时,编译器会生成一个错误。 - Ivaylo Strandjev
你说得没错,像类型安全等方面也需要考虑。我想强调的是编译器不会生成任何额外的信息,在运行时或编译时都无法在代码本身中主动使用。 - Klaus
那么也许可以删除“根本不再”的部分? - Ivaylo Strandjev
@IvayloStrandjev 如果你将枚举与任何整数进行比较(它的目的不就是与整数进行比较吗?),那么你只需要将其强制转换。if (x == (int)enum_foo) do something - Evan Carslake
我想指出的是,即使编译器本身不会为枚举生成反射信息,但可以使用一些元编程来生成该信息。 - antron

1
如果您将问题标记为C++,我建议放弃使用C的方式进行操作,因此在C ++ 中更好的方法是使用std :: vector:

有没有比使用包含所有值的数组更好的方式(size_t arr[3] ={FOO_1 , FOO_X, FOO_BAR })?

如果您将问题标记为C++,我建议放弃使用C的方式进行操作,因此在C ++ 中更好的方法是使用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};
    }
};

您可以这样使用它:

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

如果您不想在运行时创建向量,可以将其声明为静态并仅创建一次:
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};

在线演示

我知道这是一项负担,但由于没有办法迭代枚举的值,试图迭代它们的所有代码也都是一种负担。

也许在下面的答案中,您可以找到一些有用的方法来更好地迭代枚举以达到您的目标:


现代的做法是使用std::array<Alphas>或of<int>,因为计数在编译时已知,所以不需要使用vector。常规数组没有问题,它们也不是“不是c ++”。此外,您的GetAlphas()应该是const限定和noexcept限定的。 - Braynstorm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接