C/C++: какой-либо способ получить отражающие перечисления?

B.name атрибут класса, не атрибут экземпляра. Это обнаруживается в B.__dict__, но не в b = B(); b.__dict__.

различие затенено несколько, потому что при доступе к атрибуту на экземпляре класс dict является нейтрализацией. Таким образом в вышеупомянутом примере, b.name даст Вам значение B.name.

17
задан Thomas Bonini 26 November 2009 в 03:21
поделиться

8 ответов

Для этого требуется, чтобы плоды были определены во внешнем файле. Это будет содержимое fruit.cpp :

#define FRUIT(name) name
enum Fruit {
#include "fruit-defs.h"
NUM_FRUITS
};
#undef FRUIT
#define FRUIT(name) #name
const char *Fruits [] = {
#include "fruit-defs.h"
NULL
};
#undef FRUIT

И это будет fruit-defs.h :

FRUIT(Banana),
FRUIT(Apple),
FRUIT(Pear),
FRUIT(Tomato),

Он работает, пока значения начинаются с 0 и равны последовательный ...

Обновление: смешайте это решение с решением Ричарда Пеннингтона, используя C99, если вам нужны непоследовательные значения. То есть, что-то вроде:

// This would be in fruit-defs.h
FRUIT(Banana, 7)
...
// This one for the enum
#define FRUIT(name, number) name = number
....
// This one for the char *[]
#define FRUIT(name, number) [number] = #name
32
ответ дан 30 November 2019 в 10:22
поделиться

Метод c99, который я нашел, помогает уменьшить количество ошибок:

enum Fruit {
  APPLE,
  BANANA
};
const char* Fruits[] = {
 [APPLE] = "APPLE",
 [BANANA] = "BANANA"
};

Вы можете добавлять перечисления, даже в середине, и не нарушать старые определения. Конечно, вы все еще можете получить NULL-строки для значений, которые вы забыли.

9
ответ дан 30 November 2019 в 10:22
поделиться

Одна уловка, которую я использовал в прошлом, - это добавить дополнительное перечисление, а затем выполнить утверждение времени компиляции (например, Boost ), чтобы убедиться, что два in sync:

enum Fruit {
    APPLE,
    BANANA,

    // MUST BE LAST ENUM
    LAST_FRUIT
};

const char *FruitNames[] =
{
    "Apple",
    "Banana",
};

BOOST_STATIC_ASSERT((sizeof(FruitNames) / sizeof(*FruitNames)) == LAST_FRUIT);

Это, по крайней мере, предотвратит то, что кто-то забудет добавить и к перечислению, и к массиву имен, и сообщит им, как только они попытаются скомпилировать.

4
ответ дан 30 November 2019 в 10:22
поделиться

Один комментарий к макросу - вам не нужен отдельный файл для счетчиков. Просто используйте другой макрос:

#define FRUITs \ 
    FRUIT(Banana), \ 
    FRUIT(Apple), \ 
    FRUIT(Pear), \ 
    FRUIT(Tomato)

(хотя я бы, вероятно, оставил запятые и при необходимости включил их в макрос FRUIT).

4
ответ дан 30 November 2019 в 10:22
поделиться

Что, если бы вы сделали что-то подобное?

enum Fruit {
  Apple,
  Banana,
  NumFruits
};

const char *Fruits[NumFruits] = {
 "Apple",
 "Banana",
};

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

Таким образом, это защищает вас от неправильного размера массива, но не помогает гарантировать правильность строк.

3
ответ дан 30 November 2019 в 10:22
поделиться

Можно создать для него структуру классов:

class Fruit { 
   int value; char const * name ; 
   protected:
   Fruit( int v, char const * n ) : value(v), name(n) {}
   public:
   int asInt() const { return value ; }
   char const * cstr() { return name ; } 
} ;
#define MAKE_FRUIT_ELEMENT( x, v ) class x : public Fruit { x() : Fruit( v, #x ) {} }

// Then somewhere:
MAKE_FRUIT_ELEMENT(Apple, 1);
MAKE_FRUIT_ELEMENT(Banana, 2);
MAKE_FRUIT_ELEMENT(Pear, 3);

Тогда у вас может быть функция, которая принимает Fruit, и она будет даже более безопасна по типу.

void foo( Fruit f ) {
  std::cout << f.cstr() << std::endl;
  switch (f.asInt()) { /* do whatever * } ;
}

Размер этой функции в 2 раза больше чем просто перечисление. Но, скорее всего, это не имеет значения.

1
ответ дан 30 November 2019 в 10:22
поделиться

Как показали другие люди, отвечавшие на вопрос, на самом деле не существует чистого («СУХОГО») способа сделать это с использованием только препроцессора C. Проблема в том, что вам нужно определить массив размера вашего перечисления, содержащий строки, соответствующие каждому значению перечисления, а препроцессор C недостаточно умен, чтобы иметь возможность это сделать. Что я делаю, так это создаю текстовый файл примерно так:

%status ok
%meaning
The routine completed its work successfully.
%

%status eof_reading_content
%meaning

The routine encountered the end of the input before it expected
to. 

%

Вот разделители меток%.

Затем Perl-скрипт, рабочая часть которого выглядит так,

sub get_statuses
{
    my ($base_name, $prefix) = @_;
    my @statuses;
    my $status_txt_file = "$base_name.txt";
    my $status_text = file_slurp ($status_txt_file);
    while ($status_text =~ 
       m/
        \%status\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\n
        \%meaning\s*(.*?)\s*\n\%\s*\n
        /gxs) {
    my ($code, $meaning) = ($1, $2);
    $code = $prefix."_$code";
    $meaning =~ s/\s+/ /g;
    push @statuses, [$code, $meaning];
    }
    return @statuses;
}

читает этот файл и записывает заголовок файл:

typedef enum kinopiko_status {
    kinopiko_status_ok,
    kinopiko_status_eof_reading_content,

и файл C:

/* Generated by ./kinopiko-status.pl at 2009-11-09 23:45. */
#include "kinopiko-status.h"
const char * kinopiko_status_strings[26] = {
"The routine completed its work successfully.",
"The routine encountered the end of the input before it expected to. ",

, используя входной файл вверху. Здесь также вычисляется число 26 путем подсчета входных строк. (На самом деле существует двадцать шесть возможных состояний.)

Затем создание файла строки состояния автоматизируется с помощью make .

1
ответ дан 30 November 2019 в 10:22
поделиться

В общем, я не люблю макрорешения, хотя признаю, что их довольно сложно избежать.

Лично я выбрал собственный класс для обертывания перечислений. Цель заключалась в том, чтобы предложить немного больше, чем традиционные перечисления (например, итерация).

Под прикрытием я использую std :: map для сопоставления перечисления с его std :: string ] коллега. Затем я могу использовать это как для итерации по перечислению, так и для "красивой печати" моего перечисления или инициализации его из строки, прочитанной в файле.

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

Кроме того, я затем использую не настоящее перечисление,

1
ответ дан 30 November 2019 в 10:22
поделиться