簡體   English   中英

有沒有辦法在不知道 c++ 的大小的情況下迭代枚舉

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

你好我有以下枚舉

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
};

我想要做的是嘗試做這樣的偽代碼:

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

有沒有辦法做到這一點?

編輯:我無法控制枚舉。 此枚舉由與第 3 部分庫不同的文件提供。 我可能可以復制它,但不能更改原始枚舉。 我想將枚舉庫的成員寫入不同的文件。

不,至少不是直接的。 枚舉實際上不是一組常量。 相反,它們是一種帶有一組命名常量的類型。 區別在於:例如42params_Solver的完全有效值,它只是沒有名稱。

啟用迭代的一種常見方法是添加一個標記值:

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          // <----
};

然后從0迭代到num_params_Solver 好消息是您可以添加另一個常量,並且num_params_Solver仍然是正確的。 限制是它僅適用於沒有自定義值的枚舉。

一般的方法

您可以通過這種方式在枚舉末尾添加一個項目:

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
};

並循環它:

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

如果您為枚舉成員分配默認值以外的值,則此解決方案不起作用。

另一種方式

您可以使用常規數組而無需添加最后一個元素。 然而,語法是相當多余的,因為你必須自己指定枚舉的成員:

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 
};

然后,您可以使用以下方法遍歷枚舉:

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

枚舉的基礎類型中可表示的所有值都是枚舉的有效值,無論它們是否被賦予名稱,並且迭代 64 位枚舉范圍可能需要比您願意等待的時間更長... :)

另一個復雜的情況是,如果您只想迭代命名的枚舉器,這是可能的,但需要更多的工作和一些考慮。 如前所述,如果您不為枚舉器提供任何自定義值,那么它們將從零開始並遞增。 但是,您可以在數字中添加間隔,可以向后跳,可以重復。 具有相同值的兩個枚舉器算作一次迭代還是每個名稱一個? 不同的情況會有不同的答案。

如果您在沒有為枚舉數自定義值的情況下執行此操作,則可以在列表末尾放置一個額外的“虛擬”值並將其視為枚舉數的計數。 但是,如果您有間隙、重復或從 0 以外的值開始,這將是錯誤的。如果有人在虛擬值之后添加新的枚舉數值,它也可能會失敗。

有一些 3rd 方庫可以幫助解決這個問題。 如果您不介意一些額外的代碼,“Better Enums”開源庫是非常有用的僅標頭庫。 https://github.com/aantron/better-enums它以良好的語法為您的枚舉提供元數據,允許迭代器、范圍用於循環使用、轉換為字符串名稱/從字符串名稱轉換等等。

C++ 不為您嘗試做的事情提供內在支持。

您可以添加一些樣板來完成您想要的,以便使用枚舉范圍的代碼不知道枚舉。 在代碼示例中,該知識封裝在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";
    }
}

不。反射提案,你想要什么,可能會在 [c++23] 中出現。

沒有它,您可以將enum (可能引用它)復制粘貼到數組中,然后遍歷副本。

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

然后只做一個 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);
}

活生生的例子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM