Есть ли простой способ конвертировать перечисление C ++ в строку? - PullRequest
113 голосов
/ 14 октября 2008

Предположим, у нас есть некоторые именованные перечисления:

enum MyEnum {
      FOO,
      BAR = 0x50
};

То, что я нашел в Google, - это скрипт (любой язык), который сканирует все заголовки в моем проекте и генерирует заголовок с одной функцией на каждое перечисление.

char* enum_to_string(MyEnum t);

И реализация с чем-то вроде этого:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

Гоча действительно с перечислениями typedefed и безымянными перечислениями в стиле C. Кто-нибудь знает что-нибудь для этого?

РЕДАКТИРОВАТЬ: Решение не должно изменять мой источник, за исключением сгенерированных функций. Перечисления в API, поэтому использование решений, предложенных до сих пор, просто не вариант.

Ответы [ 35 ]

72 голосов
/ 14 октября 2008

X-макросы - лучшее решение. Пример:

#include <iostream>

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

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

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.
46 голосов
/ 14 октября 2008

Вы можете проверить GCCXML .

Запуск GCCXML на вашем примере кода приводит к:

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

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

41 голосов
/ 26 октября 2008

@ hydroo: без дополнительного файла:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};
32 голосов
/ 14 октября 2008

Я обычно создаю массив C с именами в том же порядке и положении, что и значения enum.

например.

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

, тогда вы можете использовать массив в тех местах, где вам нужно удобочитаемое значение, например,

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

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

#define printword(XX) cout << #XX;
printword(red);

выведет «красный» на стандартный вывод. К сожалению, для переменной это не сработает (так как вы получите имя переменной)

9 голосов
/ 01 мая 2014

У меня невероятно простой в использовании макрос, который делает это абсолютно СУХОЙ модой. Он включает в себя различные макросы и простую магию синтаксического анализа. Здесь идет:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

Чтобы использовать это в своем коде, просто выполните:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
8 голосов
/ 14 октября 2008

QT может получить (из-за компилятора мета-объекта): ссылка

7 голосов
/ 20 июля 2012

Я только что изобрел это колесо сегодня и решил поделиться им.

Эта реализация не требует каких-либо изменений в коде, который определяет константы, которые могут быть перечислениями или #define s или чем-то еще, что превращается в целое число - в моем случае я имел символы, определенные условия других символов. Это также хорошо работает с разреженными значениями. Он даже позволяет использовать несколько имен для одного и того же значения, всегда возвращая первое. Единственным недостатком является то, что вам необходимо составить таблицу констант, которая может устареть, например, при добавлении новых.

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

Пример того, как вы будете его использовать:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

Функция IdToName полагается на std::lower_bound для быстрого поиска, который требует сортировки таблицы. Если первые две записи в таблице не в порядке, функция сортирует их автоматически.

Редактировать: Комментарий заставил меня задуматься о другом способе использования того же принципа. Макрос упрощает генерацию большого оператора switch.

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}
6 голосов
/ 02 ноября 2012
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Дальнейшее обсуждение этого метода

Рекомендации по препроцессору для новичков

5 голосов
/ 23 июня 2009

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

в файле myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

в main.cpp

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

Это не const, но это удобно.

Вот еще один способ, который использует функции C ++ 11. Это const, не наследует контейнер STL и немного аккуратнее:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}
5 голосов
/ 19 июня 2014

Это можно сделать в C ++ 11

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...