简体   繁体   English

有没有办法在不知道 c++ 的大小的情况下迭代枚举

[英]Is there a way to iterate over an enum without knowing its size in c++

Hello I have the following enum你好我有以下枚举

enum params_Solver {
  params_Solver_Lorem,
  params_Solver_Ipsum,
  params_Solver_Simply,
  params_Solver_Dummy,
  params_Solver_Test,
  params_Solver_Typesetting,
  params_Solver_Industry,
  params_Solver_Scrambled
};

what I want to do is try to doing something like this pseudocode:我想要做的是尝试做这样的伪代码:

for (auto enum_member: params_Solver)
{
    print(index, enum_member); // output looks like this: "0, params_Solver_Lorem", "1, params_Solver_Ipsum" etc
}

Is there anyway to achieve this?有没有办法做到这一点?

Edit: I do not have the control over enum.编辑:我无法控制枚举。 This enum is provided by a different file from a 3rd part library.此枚举由与第 3 部分库不同的文件提供。 I can probably copy it but not change the original enum.我可能可以复制它,但不能更改原始枚举。 I want to write the members of the enum library to a different file.我想将枚举库的成员写入不同的文件。

No. At least not directly.不,至少不是直接的。 Enums are actually not a set of constants.枚举实际上不是一组常量。 Rather they are a type that comes with a set of named constants.相反,它们是一种带有一组命名常量的类型。 Difference is: for example 42 is a completely valid value of params_Solver , it just has no name.区别在于:例如42params_Solver的完全有效值,它只是没有名称。

A common way to enable iteration is to add a sentinel value:启用迭代的一种常见方法是添加一个标记值:

enum params_Solver {
  params_Solver_Lorem,
  params_Solver_Ipsum,
  params_Solver_Simply,
  params_Solver_Dummy,
  params_Solver_Test,
  params_Solver_Typesetting,
  params_Solver_Industry,
  params_Solver_Scrambled,
  num_params_Solver          // <----
};

And then iterate from 0 till num_params_Solver .然后从0迭代到num_params_Solver The nice thing is that you can add another constant and num_params_Solver will still be correct.好消息是您可以添加另一个常量,并且num_params_Solver仍然是正确的。 The limitation is that it only works for enums without custom values.限制是它仅适用于没有自定义值的枚举。

Common way一般的方法

You could add an item at the end of your enumeration this way:您可以通过这种方式在枚举末尾添加一个项目:

enum params_Solver {
  params_Solver_Lorem,
  params_Solver_Ipsum,
  params_Solver_Simply,
  params_Solver_Dummy,
  params_Solver_Test,
  params_Solver_Typesetting,
  params_Solver_Industry,
  params_Solver_Scrambled,
  Last
};

And loop over it with:并循环它:

for (int i = params_Solver_Lorem; i != Last; i++) {
    // some code
}

This solution does not work if you assign values to your enum members other than the default ones though.如果您为枚举成员分配默认值以外的值,则此解决方案不起作用。

Other way另一种方式

You can use a regular array without having to add a last element.您可以使用常规数组而无需添加最后一个元素。 However the syntax is quite redundant as you have to specify yourself the members of the enum:然而,语法是相当多余的,因为你必须自己指定枚举的成员:

constexpr params_Solver members[] {
    params_Solver_Lorem,
    params_Solver_Ipsum,
    params_Solver_Simply,
    params_Solver_Dummy,
    params_Solver_Test,
    params_Solver_Typesetting,
    params_Solver_Industry,
    params_Solver_Scrambled 
};

You can then iterate over the enum using:然后,您可以使用以下方法遍历枚举:

for (auto m: members) {
    // Some code
}

All values representable in the underlying type of the enum are valid values of the enum, whether they are given a name or not, and iterating over a 64-bit enum range might take longer than you'd care to wait... :)枚举的基础类型中可表示的所有值都是枚举的有效值,无论它们是否被赋予名称,并且迭代 64 位枚举范围可能需要比您愿意等待的时间更长... :)

Another complication, if you mean to iterate over just the named enumerators, that's possible, but takes more work, and some considerations.另一个复杂的情况是,如果您只想迭代命名的枚举器,这是可能的,但需要更多的工作和一些考虑。 As already stated, if you do not give any custom values to your enumerators, then they will start at zero and increment.如前所述,如果您不为枚举器提供任何自定义值,那么它们将从零开始并递增。 However, you can put gaps in the numbers, you can jump backwards, you can have repeats.但是,您可以在数字中添加间隔,可以向后跳,可以重复。 Do two enumerators with the same value count as one iteration or one per name?具有相同值的两个枚举器算作一次迭代还是每个名称一个? Different situations will have different answers.不同的情况会有不同的答案。

If you do this without custom values for your enumerators, you can put an extra "dummy" value at the end of the list and treat it as the count of the enumerators.如果您在没有为枚举数自定义值的情况下执行此操作,则可以在列表末尾放置一个额外的“虚拟”值并将其视为枚举数的计数。 But it will be wrong if you have gaps, duplicates, or start at a value other than 0. It also can fail if someone adds new enumerator values after the dummy value.但是,如果您有间隙、重复或从 0 以外的值开始,这将是错误的。如果有人在虚拟值之后添加新的枚举数值,它也可能会失败。

There are some 3rd party libraries that can help with this.有一些 3rd 方库可以帮助解决这个问题。 If you don't mind a bit of extra code, the "Better Enums" open source library is very useful header-only library.如果您不介意一些额外的代码,“Better Enums”开源库是非常有用的仅标头库。 https://github.com/aantron/better-enums It gives meta data over your enums with a nice syntax, allowing iterators, ranged for loop use, converting to/from string names and some more. https://github.com/aantron/better-enums它以良好的语法为您的枚举提供元数据,允许迭代器、范围用于循环使用、转换为字符串名称/从字符串名称转换等等。

C++ does not provide intrinsic support for what you are trying to do. C++ 不为您尝试做的事情提供内在支持。

You can add a some boilerplate to accomplish what you want, so that the code that is using the range of the enum is not aware of the enums.您可以添加一些样板来完成您想要的,以便使用枚举范围的代码不知道枚举。 In the code example, that knowledge is encapsulated in the params_Solver_Range helper class.在代码示例中,该知识封装在params_Solver_Range帮助程序 class 中。

#include <iostream>
#include <stdexcept>
#include <utility>

using std::ostream;
using std::cout;
using std::underlying_type_t;
using std::logic_error;

namespace {

enum class params_Solver {
    Lorem,
    Ipsum,
    Simply,
    Dummy,
    Test,
    Typesetting,
    Industry,
    Scrambled
};

auto operator<<(ostream& out, params_Solver e) -> ostream& {
#define CASE(x) case params_Solver::x: return out << #x
    switch(e) {
        CASE(Lorem);
        CASE(Ipsum);
        CASE(Simply);
        CASE(Dummy);
        CASE(Test);
        CASE(Typesetting);
        CASE(Industry);
        CASE(Scrambled);
    }
#undef CASE

    throw logic_error("unknown params_Solver");
}

auto operator+(params_Solver e) {
    return static_cast<underlying_type_t<decltype(e)>>(e);
}

auto operator++(params_Solver& e) -> params_Solver& {
    if (e == params_Solver::Scrambled) throw logic_error("increment params_Solver");
    e = static_cast<params_Solver>(+e + 1);
    return e;
}

class params_Solver_Range {
    bool done = false;
    params_Solver iter = params_Solver::Lorem;

public:
    auto begin() const -> params_Solver_Range const& { return *this; }
    auto end() const -> params_Solver_Range const& { return *this; }
    auto operator*() const -> params_Solver { return iter; }
    bool operator!=(params_Solver_Range const&) const { return !done; }
    void operator++() {
        if (done) throw logic_error("increment past end");
        if (iter == params_Solver::Scrambled) done = true;
        else ++iter;
    }
};

} // anon

int main() {
    for (auto e : params_Solver_Range()) {
        cout << +e << ", " << e << "\n";
    }
}

No. The reflection proposal, which does what you want, may arrive in [c++23].不。反射提案,你想要什么,可能会在 [c++23] 中出现。

Without it, you can copy paste the enum , possibly quoting it, into an array, and iterating over the copy.没有它,您可以将enum (可能引用它)复制粘贴到数组中,然后遍历副本。

constexpr char const* members[] {
  "params_Solver_Lorem",
  "params_Solver_Ipsum",
  //etc
};

then just do a for loop over that.然后只做一个 for 循环。

for (char const* const& enum_member: members)
{
  auto index = static_cast<long long unsigned>(&enum_member-members);
  printf("%llu, %s\n", index, enum_member);
}

Live example .活生生的例子

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

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