Enum в C ++, как Enum в Ada? - PullRequest
       22

Enum в C ++, как Enum в Ada?

2 голосов
/ 19 ноября 2008

В какой-то момент я посмотрел на реализацию класса / шаблона в C ++, который бы поддерживал Enum, который вел себя так же, как в Ada. Прошло некоторое время с тех пор, как я думал об этой проблеме, и мне было интересно, кто-нибудь когда-нибудь решал эту проблему?

EDIT:

Приношу свои извинения, я должен уточнить, какие функциональные возможности, по моему мнению, были полезны в реализации Enum для Ada. Учитывая перечисление

type fruit is (apple, banana, cherry, peach, grape);

Мы знаем, что фрукт является одним из перечисленных фруктов: яблоко, банан, вишня, персик, виноград. Ничего особо не отличается от C ++.

Что очень полезно, так это следующие функциональные возможности, которые вы получаете с каждым перечислением в Ada без дополнительной работы:

  • распечатка перечисляемого значения генерирует строковую версию
  • Вы можете увеличивать перечисляемую переменную
  • Вы можете уменьшить перечисляемую переменную

Надеюсь, это определит проблему немного подробнее.


Примечания добавлены из комментариев :

Полезные функции перечислений Ada

  • Первое значение в перечислении: fruit'first, что дает apple.
  • Последнее значение в перечислении: fruit'last, что дает grape.
  • Операция приращения равна fruit'succ(apple), что дает banana.
  • Операция декремента равна fruit'pred(cherry), что также дает banana.
  • Преобразование из перечисления в целое число: fruit'pos(cherry), которое возвращает 2, поскольку Ada использует нумерацию на основе 0.
  • Преобразование из целого в перечисление: fruit'val(2), которое возвращает cherry.
  • Преобразование из перечисления в строку: fruit'Image(apple), которое возвращает строку (в верхнем регистре) "APPLE".
  • Преобразование из строки в перечисление: fruit'Value("apple"), которое возвращает значение apple.

См. Также связанные вопросы SO:

Ответы [ 8 ]

3 голосов
/ 20 ноября 2008

Хорошо, давайте ненадолго оставим C ++ в стороне. C ++ - это просто расширенный набор C (это означает, что все, что можно сделать в C, можно сделать и в C ++). Итак, давайте сосредоточимся на обычном C (потому что это язык, который я хорошо знаю). C имеет перечисления:

enum fruit { apple, banana, cherry, peach, grape };

Это совершенно допустимый C, и значения являются смежными, и яблоко имеет значение ноль, а банан имеет значение яблоко + 1. Вы можете создавать перечисления с дырками, но только если вы явно делаете отверстия как это

enum  fruit { apple = 0, banana, cherry = 20, peach, grape };

В то время как яблоко - 0, а банан - 1, вишня - 20, таким образом, персик - 21, а виноград - 22, и все между 1 и 20 неопределено. Обычно вы не хотите дыр. Вы можете сделать следующее:

enum fruit { apple = 0, banana, cherry, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");

Это напечатает ДА. Вы также можете сделать следующее:

enum fruit { apple = 0, banana, cherry = 20, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");

При этом будет напечатано NO, и значение myFruit будет не таким, как любая из констант перечисления.

Кстати, во избежание того, что вы должны сказать «enum fruit myFruit», вы можете избежать перечисления с помощью typedef. Просто используйте «typedef enum fruit fruit»; на собственной линии. Теперь вы можете сказать «Фрукты myFruit» без перечисления впереди. Это часто делается непосредственно при определении перечисления:

typedef enum fruit { apple = 0, banana, cherry, peach, grape } fruit;

fruit myFruit;

Недостаток в том, что вы больше не знаете, что фрукт - это перечисление, это может быть объект, структура или что-то еще. Я обычно избегаю такого типа typedefs, я скорее пишу enum перед, если enum, и struct перед, если struct (я просто буду использовать их здесь, потому что это выглядит лучше).

Получить строковое значение невозможно. Во время выполнения перечисление - это просто число. Это означает, что это невозможно, если вы не знаете, что это за перечисление (0 может быть яблоком, но это может быть и другая вещь из другого набора перечислений). Однако, если вы знаете, что это фрукт, тогда легко написать функцию, которая сделает это за вас. Препроцессор - твой друг: -)

typedef enum fruit {
    apple = 0,
    banana,
    cherry,
    peach,
    grape
} fruit;

#define STR_CASE(x) case x: return #x
const char * enum_fruit_to_string(fruit f) {
    switch (f) {
        STR_CASE(apple); STR_CASE(banana); STR_CASE(cherry);
        STR_CASE(peach); STR_CASE(grape);
    }
    return NULL;
}
#undef STR_CASE

static void testCall(fruit f) {
    // I have no idea what fruit will be passed to me, but I know it is
    // a fruit and I want to print the name at runtime
    printf("I got called with fruit %s\n", enum_fruit_to_string(f));
}

int main(int argc, char ** argv) {
    printf("%s\n", enum_fruit_to_string(banana));
    fruit myFruit = cherry;
    myFruit++; // myFruit is now peach
    printf("%s\n", enum_fruit_to_string(myFruit));
    // I can also pass an enumeration to a function
    testCall(grape);
    return 0;
}

Вывод:

banana
peach
I got called with fruit grape

Это именно то, что вы хотели, или я здесь не на том пути?

2 голосов
/ 19 ноября 2008

Один из моих коллег реализовал инструмент для генерации классов, которые выполняют большинство (если не все) из того, что вы хотите:

http://code.google.com/p/enumgen/

Текущая реализация в Лиспе, но не держите это против него: -)

1 голос
/ 17 сентября 2009

Я написал enum_iterator, который делает это, вместе с макросом ENUM, используя Boost.Preprocessor:

#include <iostream>
#include "enum.hpp"

ENUM(FooEnum, 
  (N)
  (A = 1)
  (B = 2)
  (C = 4)
  (D = 8));

int main() {
  litb::enum_iterator< FooEnum, litb::SparseRange<FooEnum> > i = N, end;
  while(i != end) {
    std::cout << i.to_string() << ": " << *i << std::endl;
    ++i;
  }
}

Он объявляет enum как простой старый enum, так что вы все равно можете использовать его для «обычных» целей. Итератор может использоваться и для других обычных перечислений, которые имеют последовательные значения, поэтому он имеет второй параметр шаблона, который по умолчанию равен litb::ConsequtiveRange<>. Это соответствует требованиям двунаправленного итератора.

Глупый код можно скачать здесь

1 голос
/ 20 ноября 2008

Если вы заинтересованы в enumgen, я сделал простую демонстрацию с твой пример. Как уже упоминалось, я реализовал это с помощью распространенный шрифт, поэтому входной файл, который вы пишете, шустрый, но я очень старался сделать синтаксис разумным.

Вот оно:

$ cat Fruit.enum
(def-enum "Fruit" (("apple")
                   ("banana")
                   ("cherry")
                   ("peach")
                   ("grape")
                   ("INVALID_")))

$ enumgen Fruit.enum
Using clisp
;; Loading file /tmp/enumgen/enumgen.lisp ...
;; Loaded file /tmp/enumgen/enumgen.lisp
loading def file:
;; Loading file /tmp/enumgen/enumgen.def ...
;; Loaded file /tmp/enumgen/enumgen.def
generating output:
  Fruit.cpp
  Fruit.ipp
  Fruit.hpp
DONE

Чтобы просмотреть сгенерированный код, посетите этот URL: http://code.google.com/p/enumgen/source/browse/#svn/trunk/demo

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

Например, по умолчанию он представляет имена строк, используя std :: string, но он может использовать char const * или любую пользовательскую строку класс дал немного усилий.

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

Вы можете явно указать значения для перечислений, а они не должен быть уникальным. (Дубликаты являются неявными псевдонимами для предыдущего enum с тем же значением.)

Кроме того, вы можете перебирать все уникальные значения и для каждого значение по всем его псевдонимам, что полезно, если вы хотите сгенерируйте для них «обертки» на языке сценариев, как мы используем ruby.

Если вы заинтересованы в использовании этого и у вас есть вопросы, не стесняйтесь свяжитесь со мной по электронной почте. (Потому что в Gmail).

Надеюсь, это поможет. (Существует не так много документации, кроме набор тестов, демонстрационный код и исходный код, если вам это важно.)

Chris

1 голос
/ 19 ноября 2008

вы можете взглянуть на перечисление java (http://madbean.com/2004/mb2004-3/) и эту идею: http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern

1 голос
/ 19 ноября 2008

Нет простого способа сделать это в C ++, не в последнюю очередь потому, что константы перечисления не обязательно должны быть уникальными или смежными. Преобразование из значения в строку также нетривиально; известные мне решения включают хакерство препроцессора C / C ++ - и это уничижительное использование термина хакерство.

Я испытываю желание сказать "Нет"; Я не уверен, что это правильно, но это, безусловно, нетривиально.

0 голосов
/ 04 ноября 2009

Это невыпущенное программное обеспечение, но, кажется, BOOST_ENUM от Фрэнка Лауба могло бы соответствовать всем требованиям. Что мне нравится в этом, так это то, что вы можете определить перечисление в пределах класса, которое большинство перечислений на основе макросов обычно не позволяет вам сделать. Он находится в Boost Vault по адресу: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& С 2006 года он не видел никаких разработок, поэтому я не знаю, насколько хорошо он компилируется с новыми выпусками Boost. Посмотрите в libs / test пример использования. Существует также Boost smart_enum (также не выпущен). Это делает итератор частью вашего вопроса, но не выводит строку. http://cryp.to/smart-enum/

0 голосов
/ 19 ноября 2008

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

Эта библиотека обеспечивает более гибкое увеличение и уменьшение.

Библиотека enum_rev4.6.zip в Boost Vault обеспечивает простое преобразование строк. Похоже, что он поддерживает увеличение и уменьшение с использованием итераторов (что, вероятно, менее удобно, но работает). Это в основном недокументировано, хотя каталог libs / test содержит хороший пример кода.

...