Аннотация:
В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
Здравствуйте, Вьюков Д. С., Тепляков С. В., Вы писали:
спасибо
самое большое решение тянет монстра под названием boost
радует объективный подход — перечисление как плюсов, так и минусов каждого решения
можно было еще осветить подход c++0x (привести пример кода, решающего те же самые задачи)
читатель мог бы взвесить что ему ближе — использовать boost или c++0x
и оценить, решает ли новый стандарт обыденные проблемы енумов или снова надо все обкладывать макросами
ВДС>Авторы: ВДС> SergeyT. ВДС> remark ВДС>Аннотация: ВДС>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
Спасибо статья хорошая.
В тему к перечислениям, Я активно использую вот такой вот классик. linear enum converter
А вот его более свежая версия.
Добавилось то что теперь появились макросы которыми можно связывать не константные данные состояние которых можно изменить при вызове функции преобразования константы из enum-a к данным.
Мне например такое нужно было для отложенной подгруздки данных в приложение.
А почему не упомянута предлагавшаяся в Буст библиотека Boost.Enum, раз уж дан обзор? http://www.boostpro.com/vault/, файл enum_rev4.6.zip (последняя версия, от 17.01.2006)
ВДС>Авторы: ВДС> SergeyT. ВДС> remark ВДС>Аннотация: ВДС>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
Забавные оценки.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Вьюков Д. С., Тепляков С. В., Вы писали:
ВДС>Аннотация: ВДС>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
Вот честно не представляю, зачем может понадобиться что-то сложнее первого варианта, описанного в статье, кроме академических изысков
То есть, по мне, так вот это:
решает большинство задач встречающихся в народном хозяйстве,
включая автокомплит/именование и перечисление всех элементов (0 < i < Option_Count).
Ради чего плодить кучу безумных макросов??
Проблемы которые вижу со всеми остальными вариантами:
— Часто енумы используются как "флаги" ([Flags] в C#) , однако вариант (Option::One|Option::Two) не скомпилируется, нужен кастинг или еще экран кода с операторами "|".
— Такие енумы несовместимы с кодом на C, или с кодом на IDL.
— Для определения строготипизированного енума тупо придется писать больше неудобочитаемого текста.
Здравствуйте, bnk, Вы писали:
ВДС>>Аннотация: ВДС>>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика. bnk>Вот честно не представляю, зачем может понадобиться что-то сложнее первого варианта, описанного в статье, кроме академических изысков bnk>То есть, по мне, так вот это: bnk>
bnk>решает большинство задач встречающихся в народном хозяйстве, bnk>включая автокомплит/именование и перечисление всех элементов (0 < i < Option_Count). bnk>Ради чего плодить кучу безумных макросов??
Решает, но иногда надо связать с енумом строковые литералы, что, имхо, и продемонстрировано. Хотя я считаю пример надуманным, т.к. если и надо строковые литералы к енумам привязать, то либо это надо делать в рантайме, либо до него. Если до него, то никакие типы из рантайма (std::string) не должны использоваться, если в рантайме, то зачем тогда макросы? Имхо, если уж так хочется в точке объявления всё писать, то незачем тащить std::string туда, где он не нужен, можно спокойно обойтись boost::preprocessor в полной мере.
Примерно так можно:
#include <stdio.h>
template<const char* (*pfunc)()>
struct EnumStringValueA
{
const char* value;
EnumStringValueA()
{
value = (*pfunc)();
}
};
class MyEnumImpl
{
public:
enum type { Value1 = 1, Value2 = 10 };
protected:
template<int dummy>
class names_tmpl
{
public:
static const char* get_Value1() { return"Value1"; }
static const char* get_Value2() { return"Value2"; }
//Names are the same as names from wrapped enumeration.static EnumStringValueA<&names_tmpl::get_Value1> Value1;
static EnumStringValueA<&names_tmpl::get_Value2> Value2;
};
template<int dummy>
class descs_tmpl
{
public:
static const char* get_Value1() { return"My desc 1"; }
static const char* get_Value2() { return"My desc 2"; }
//Names are the same as names from wrapped enumeration.static EnumStringValueA<&descs_tmpl::get_Value1> Value1;
static EnumStringValueA<&descs_tmpl::get_Value2> Value2;
};
public:
typedef names_tmpl<0> names;
typedef descs_tmpl<0> descs;
};
template<int dummy>
EnumStringValueA<&MyEnumImpl::names_tmpl<dummy>::get_Value1> MyEnumImpl::names_tmpl<dummy>::Value1;
template<int dummy>
EnumStringValueA<&MyEnumImpl::names_tmpl<dummy>::get_Value2> MyEnumImpl::names_tmpl<dummy>::Value2;
template<int dummy>
EnumStringValueA<&MyEnumImpl::descs_tmpl<dummy>::get_Value1> MyEnumImpl::descs_tmpl<dummy>::Value1;
template<int dummy>
EnumStringValueA<&MyEnumImpl::descs_tmpl<dummy>::get_Value2> MyEnumImpl::descs_tmpl<dummy>::Value2;
int main()
{
//Example of usage #1.
printf("%s = {%i, \"%s\"};\n",MyEnumImpl::names::Value1.value,MyEnumImpl::Value1,MyEnumImpl::descs::Value1.value);
//Example of usage #2.
printf("%s = {%i, \"%s\"};\n",MyEnumImpl::names::get_Value2(),MyEnumImpl::Value2,MyEnumImpl::descs::get_Value2());
return 0;
}
Естесственно, MyEnumImpl вставляется через примерно такой макрос:
#define DEFINE_STRICT_ENUM_BLA_BLA(enumClassName,(Value1,1,"My desc 1")(Value2,10,"My desc 2")) \
class enumClassName ...
//enumClassName здесь это сгенерированный на лету MyEnumImpl.
Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>Решает, но иногда надо связать с енумом строковые литералы, что, имхо, и продемонстрировано. Хотя я считаю пример надуманным, т.к. если и надо строковые литералы к енумам привязать, то либо это надо делать в рантайме, либо до него. Если до него, то никакие типы из рантайма (std::string) не должны использоваться, если в рантайме, то зачем тогда макросы? Имхо, если уж так хочется в точке объявления всё писать, то незачем тащить std::string туда, где он не нужен, можно спокойно обойтись boost::preprocessor в полной мере.
Я за рантайм. Ну не растут у нас лимоны, ну и фиг с ними, какой смысл их в горшочках выращивать... Разве что из спортивного интереса.
Сравним код выше с очевидным вариантом. Какой будет легче поддерживать? В общем, не доходят до меня пока эти изыски...
Здравствуйте, bnk, Вы писали:
bnk>Я за рантайм. Ну не растут у нас лимоны, ну и фиг с ними, какой смысл их в горшочках выращивать... Разве что из спортивного интереса. bnk>Сравним код выше с очевидным вариантом. Какой будет легче поддерживать? В общем, не доходят до меня пока эти изыски... bnk>enum Option { Option_One, Option_Two, Option_Count };
Здесь можно забыть обновить Option_Count, если значения явно заданы для енума и не последовательно идут.
bnk>const char* getOptionValue(Option option) bnk>{ bnk> switch (option) bnk> { bnk> case Option_One: return "Value1"; bnk> case Option_Two: return "Value2"; bnk> } bnk>}
Здесь можно забыть добавить case для свитча.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
bnk>>enum Option { Option_One, Option_Two, Option_Count }; V>Здесь можно забыть обновить Option_Count, если значения явно заданы для енума и не последовательно идут.
Если значения явно заданы для енума и непоследовательно,
то Option_Count вообще не имеет никакого смысла, и его нужно тупо удалить.
bnk>> case Option_One: return "Value1"; bnk>> case Option_Two: return "Value2";
V>Здесь можно забыть добавить case для свитча.
Безусловно. Это минус. При добавлении нового элемента можно забыть обновить.
Однако с другой стороны, зачем-то же новый элемент добавляется, и все равно (скорее всего) придется просматривать места где этот енум используется.
А минимальную страховку от забывчивости конечно надо добавить.
switch (option)
{
case Option_One: return"value1";
case Option_Two: return"value2";
default:
assert(!"boo");
return"";
}
Здравствуйте, bnk, Вы писали:
bnk>>>enum Option { Option_One, Option_Two, Option_Count }; V>>Здесь можно забыть обновить Option_Count, если значения явно заданы для енума и не последовательно идут. bnk>Если значения явно заданы для енума и непоследовательно, bnk>то Option_Count вообще не имеет никакого смысла, и его нужно тупо удалить.
Даже в этом случае не факт, значения могут перебираться через заранее проинициализированный массив, а for без размера бесполезен.
bnk>>> case Option_One: return "Value1"; bnk>>> case Option_Two: return "Value2"; V>>Здесь можно забыть добавить case для свитча. bnk>Безусловно. Это минус. При добавлении нового элемента можно забыть обновить. bnk>Однако с другой стороны, зачем-то же новый элемент добавляется, и все равно (скорее всего) придется просматривать места где этот енум используется.
Вот именно, а так есть гарантии что не забудете, т.е. "либо всё, либо ничего".
bnk>А минимальную страховку от забывчивости конечно надо добавить.
Ну это само собой
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, zaufi, Вы писали:
Z>??? <- это мой молчаливый вопрос оставшийся после прочтения примечания...
Примечание относится к следующей цитате Страуструпа:
«Размер (sizeof) перечисления является размером некоторого интегрального типа, который в состоянии содержать весь диапазон значений перечисления. Результат не больше, чем sizeof(int) при условии, что элементы перечисления представимы в виде int или unsigned int. Например, sizeof(e1) может равняться 1 или 4, но не 8 на машине, где sizeof(int) == 4.»
Мы в примечании расширили исходную цитату Страуструпа добавив туда еще и 2, т.е. было "1 или 4, но не 8", стало "1, 2 или 4, но не 8", при этом завершение исходной фразы "где sizeof(int) == 4" мы опустили, за ненадобностью; других целей у примечания не было. А в вашем же случае, sizeof(int) как раз, наверное, и равен 8. Так ведь?
V>Здесь можно забыть добавить case для свитча.
В принципе, здесь неплохо работает кодогенерация из какого-нибудь описательного формата в C++, как pre-build шаг.
Да здравствует мыло душистое и веревка пушистая.
Re[3]: Собираются ли последние версии QT в Visual Studio ?
V>Решает, но иногда надо связать с енумом строковые литералы, что, имхо, и продемонстрировано. Хотя я считаю пример надуманным, т.к. если и надо строковые литералы к енумам привязать, то либо это надо делать в рантайме, либо до него. Если до него, то никакие типы из рантайма (std::string) не должны использоваться, если в рантайме, то зачем тогда макросы? Имхо, если уж так хочется в точке объявления всё писать, то незачем тащить std::string туда, где он не нужен, можно спокойно обойтись boost::preprocessor в полной мере. V>Примерно так можно: V>
V>Естесственно, MyEnumImpl вставляется через примерно такой макрос: V>
V>#define DEFINE_STRICT_ENUM_BLA_BLA(enumClassName,(Value1,1,"My desc 1")(Value2,10,"My desc 2")) \
V>class enumClassName ...
V>//enumClassName здесь это сгенерированный на лету MyEnumImpl.
V>
V>Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума.
ужос! это пример как из простого сделать нечитабельно-неподдерживаемое сложное?
помню у нас был один такой умелец, закрылся в своей комнатушке и писал модуль 3 месяца руководствуясь подобными парадигмами, в репозиторий не коммитил а потом выдал результат, что все упали. Модуль проработал в релизе до первого баг-репорта, потом пришлось все выкинуть и переписать заново (так было быстрее).
Здравствуйте, rsdntchkru, Вы писали:
V>>... V>>Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума. R>ужос! это пример как из простого сделать нечитабельно-неподдерживаемое сложное?
Вы вариант remark'а видели? Так ещё больший ужос, лучше вам в программирование нос не совать, там много ужософ ходят..
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[5]: Собираются ли последние версии QT в Visual Studio ?
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, rsdntchkru, Вы писали:
V>>>... V>>>Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума. R>>ужос! это пример как из простого сделать нечитабельно-неподдерживаемое сложное? V>Вы вариант remark'а видели? Так ещё больший ужос, лучше вам в программирование нос не совать, там много ужософ ходят..
Да уж, наверно надо было не совать. К сожалению как 17 лет назад засунул, так все там. И все еще не перстаю порой удивляться ширине пытливой мысли некоторых программистов типа как приведенной в варианте 4 этой статьи. Если из обычного enuma такую тему можно было развить, то что будет если они код для пузырьковой сортировки опишут? Роман на 500 листов
Re[6]: Собираются ли последние версии QT в Visual Studio ?
Здравствуйте, rsdntchkru, Вы писали:
V>>>>... V>>>>Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума. R>>>ужос! это пример как из простого сделать нечитабельно-неподдерживаемое сложное? V>>Вы вариант remark'а видели? Так ещё больший ужос, лучше вам в программирование нос не совать, там много ужософ ходят.. R>Да уж, наверно надо было не совать. К сожалению как 17 лет назад засунул, так все там. И все еще не перстаю порой удивляться ширине пытливой мысли некоторых программистов типа как приведенной в варианте 4 этой статьи. Если из обычного enuma такую тему можно было развить, то что будет если они код для пузырьковой сортировки опишут? Роман на 500 листов
Не будет романов, std::sort решает в большинстве, похоже за 17 лет вы не в курсе про него
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, rsdntchkru, Вы писали:
V>>>>>... V>>>>>Можно ещё наворотов добавить, вроде итератора по значениям енума (через boost::mpl) и перегрузить операторы, чтобы делать операции над значениями енума. R>>>>ужос! это пример как из простого сделать нечитабельно-неподдерживаемое сложное? V>>>Вы вариант remark'а видели? Так ещё больший ужос, лучше вам в программирование нос не совать, там много ужософ ходят.. R>>Да уж, наверно надо было не совать. К сожалению как 17 лет назад засунул, так все там. И все еще не перстаю порой удивляться ширине пытливой мысли некоторых программистов типа как приведенной в варианте 4 этой статьи. Если из обычного enuma такую тему можно было развить, то что будет если они код для пузырьковой сортировки опишут? Роман на 500 листов V>Не будет романов, std::sort решает в большинстве, похоже за 17 лет вы не в курсе про него
Не удивлюсь если напишут. А вы, я уверен, придумаете на это еще более изощренный вариант
Здравствуйте, Вьюков Д. С., Тепляков С. В., Вы писали:
ВДС>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
Мне трепетно возражать истинным гуру, но по моему скромному мнению уже за это:
switch(c.GetValue())
{
//Использовать выражение Color::Red().GetValue() в блоках
//case нельзя, т.к. это выражение не является константой
//времени компиляции.
//Специально для этой цели внутри каждого класса
//определяется внутреннее перечисление, которое содержит
//все значения констант с завершающим символом "_"case Color::Red_:
std::cout << "Hello, Red color!" << std::endl;
break;
}
Весь вариант 4 можно смело отправлять в раздел специальных решений под конкретную бизнес-задачу. Причем условия этой бизнес задачи прямо указаны в статье:
Вариант 4. Строгое перечисление
Это наиболее функциональный, но и наиболее сложный в реализации вариант, который позволяет следующее:
...
Цитировать весь текст не буду, но по-моему решается как раз очень конкретная задача, к общему "типу перечислений и использованию" отношения прямо не имеющая. И это, имхо, правильно. При необходимости оперировать в пространстве задачи неким "специальным типом перечисления" его и следует создавать и оперировать им как специальным нашим бизнес-объектом с нужными нам свойствами. Но это будет "наш бизнес-объекти", а не "перечисление вообще", имхо. Может, конечно, просто пример цвета для этого неудачен — нужен более конкретный пример использования, который показал бы преимущества реализации н.н. 4 над другими, более простыми реализациями при использовании в данной конкретной задаче, но сейчас, имхо, тема как-то нераскрыта. Городить подобное только ради перечисленных задач кажется слегка тяжеловатым.
Очень полезным считаю раздел "Визуализация строгих перечислений" — за него отдельное спасибо!
Здравствуйте, bnk, Вы писали:
bnk>Я за рантайм. Ну не растут у нас лимоны, ну и фиг с ними, какой смысл их в горшочках выращивать... Разве что из спортивного интереса. bnk>Сравним код выше с очевидным вариантом. Какой будет легче поддерживать? В общем, не доходят до меня пока эти изыски...
Я не люблю рантайм, но и буст мы не юзаем, я также считаю что хоть и авторы статьи потрудились на славу,
но увидев такое в коде какой нить юниор сразу поседеет, думаю не стоит так пужать людей =)
В коде когда нужно связать значение со строками или другими константными типами основная проблема на мой взгляд заключается в том, что объявлять перечисление нужно в заголовке, а соответствующий константный статический массив со значениями в cpp файле, из-за чего приходится при правке скакать по разным файлам и тщательно все проверять. Кроме того массив не контролируется при доступе. На этом потребности решения моих проблем по сути и заканчиваются.
Поэтому наваял давненько такой вот костылек которым успешно и пользуюсь:
class A {
public:
enum Some {
Value0,
Value1,
Value2,
ItemsNumber
};
// Обращаемся как A::EnglishValue(i)
BEGIN_STRING_LIST(EnglishValue)
"Value0",
"Value1",
"Value2",
END_STRING_LIST()
// Обращаемся как A::RussianValue(i)
BEGIN_STRING_LIST(RussianValue)
"Валуе0",
"Валуе1",
"Валуе2",
END_STRING_LIST()
private:
// ...
};
Допилить это по вкусу сможет каждый.
Свитч лучше всетаки не использовать, ибо например VS не всегда в состоянии сгенерить для вызова функции оптимальный код обращения к таблице, в этом варианте машинный код при отключенных ассертах в Release получается такой же как и с обычным статическим массивом (то есть никаких вызовов функции по сути нет), в итоге выходит, что все как бы и без рантайма.
Так выглядит код при обращении по неизвестному на стадии компиляции индексу (индекс в edi):
Здравствуйте, bnk, Вы писали:
bnk>Здравствуйте, Вьюков Д. С., Тепляков С. В., Вы писали:
ВДС>>Аннотация: ВДС>>В данной статье рассмотрены несколько решений, позволяющих ослабить или практически полностью избавиться от недостатков перечислений (enumerations) языка программирования С++. Представленные решения, отличаются сложностью реализации и функциональностью, и могут оказаться полезными в арсенале каждого современного разработчика.
bnk>Вот честно не представляю, зачем может понадобиться что-то сложнее первого варианта, описанного в статье, кроме академических изысков bnk>То есть, по мне, так вот это:
bnk>
bnk>решает большинство задач встречающихся в народном хозяйстве, bnk>включая автокомплит/именование и перечисление всех элементов (0 < i < Option_Count). bnk>Ради чего плодить кучу безумных макросов??
bnk>Проблемы которые вижу со всеми остальными вариантами: bnk>- Часто енумы используются как "флаги" ([Flags] в C#) , однако вариант (Option::One|Option::Two) не скомпилируется, нужен кастинг или еще экран кода с операторами "|". bnk>- Такие енумы несовместимы с кодом на C, или с кодом на IDL. bnk>- Для определения строготипизированного енума тупо придется писать больше неудобочитаемого текста.
bnk>Все IMHO, конечно
Мне вариант C# like больше нравится автодополнение быстрее сработает вроде как
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, bnk, Вы писали:
bnk>>Я за рантайм. Ну не растут у нас лимоны, ну и фиг с ними, какой смысл их в горшочках выращивать... Разве что из спортивного интереса. bnk>>Сравним код выше с очевидным вариантом. Какой будет легче поддерживать? В общем, не доходят до меня пока эти изыски... bnk>>enum Option { Option_One, Option_Two, Option_Count }; V>Здесь можно забыть обновить Option_Count, если значения явно заданы для енума и не последовательно идут.
если значения идут не последовательно, то count не имеет смысла. Тогда уж Option_Max.
а для Option_Max можно воткнуть поближе к описанию енума что-либо вроде статического ассерта на тему Option_Two < Option_Max. Тогда добавление нового элемента не пройдет безнаказанным.
Здравствуйте, March_rabbit, Вы писали:
bnk>>>Я за рантайм. Ну не растут у нас лимоны, ну и фиг с ними, какой смысл их в горшочках выращивать... Разве что из спортивного интереса. bnk>>>Сравним код выше с очевидным вариантом. Какой будет легче поддерживать? В общем, не доходят до меня пока эти изыски... bnk>>>enum Option { Option_One, Option_Two, Option_Count }; V>>Здесь можно забыть обновить Option_Count, если значения явно заданы для енума и не последовательно идут. M_>если значения идут не последовательно, то count не имеет смысла. Здесь