cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

for для перечислений #62

Open Neargye opened 3 years ago

Neargye commented 3 years ago

Перенос предложения: голоса +12, -2 Автор идеи: Georgy Firsov

Хотелось бы иметь способ пройтись по всем возможным значениям перечисления. Эти значения всегда известны на этапе компиляции, поэтому данная задача не кажется слишком сложной в том числе и для расширения её области использования на compile-time.

Как можно сейчас:

enum class EMyEnum {
  Min,
  First = Min,
  Second,
  ...
  Last,
  Max
};

...

for (std::underlying_type_t<EMyEnum> eMyEnum = EMyEnum::Min; eMyEnum < EMyEnum::Max; eMyEnum++) {
   ...
}

Но это с новым кодом, а есть ещё и старый, который менять не всегда можно, что делает такой трюк невозможным. Собственно, предложение состоит в следующем: хотелось бы делать одним из следующих способов:

enum class EMyEnum {
   First,
   Second,
   ...
   Last
};

...

// (1)
for (auto eMyEnum : EMyEnum) { ... }

// (2)
for (auto eMyEnum = begin(EMyEnum); eMyEnum != end(EMyEnum); eMyEnum++) { ... }

Думаю, что можно и иные синтаксические кострукции придумать, но пока смысла в этом не вижу. Первый же вариант кажется более лаконичным.

Тут есть скользкий вопрос - перечисления вот таких типов:

// (1)
enum class EMyBadEnum1 {
  First = 0,
  Second = 2,
  Third = 6,
  Last = 7,
  Invalid = -2
};

// (2)
enum class EMyBadEnum2 {
  First,
  Second,
  Third = First
  Last
};

В обоих случаях можно разрешить данные неприятности проходом от минимального до максимального элемента (не обязательно по порядку их определения). Шаг же инкремента тут не обязательно будет равным единице, что обеспечить легко, так как на этапе компиляции значения перечисления известны.

Повторяющиеся значения перечисления считаются одинаковыми в данном контексте (т.е. в примере с EMyBadEnum2 будет 3 итерации цикла).

Для чего оно нужно:

Это поможет писать менее чувствительный к изменениям код, так как, например, при написании DLL разумно иногда кодировать различные опции в перечислении, но иногда подобный код во имя большей простоты требует написания "цикла" по перечислению, что со старым кодом (где нежелательно вводить служебные значения Min и Max) иногда превращается в набор if-else или switch, хотя можно было бы действительно обойтись циклом по данному перечислению. И вот это та ситуация, модификация кода в которой (добавление новой опции) могла бы быть облегчена подобным циклом: достаточно добавить просто значение в перечисление, без необходимости модификации существующего кода. Это один из use-кейсов.

Neargye commented 3 years ago

Andrey 14 июля 2020, 11:27 Решается reflection-ом (в частности, в Reflection TS -- https://cplusplus.github.io/reflection-ts/draft.pdf -- есть get_enumerators).