Есть ли простой способ конвертировать перечисление 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 ]

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

Проблема с ответом 0 состоит в том, что двоичные значения перечисления не обязательно начинаются с 0 и не обязательно являются смежными.

Когда мне это нужно, я обычно:

  • вывести определение enum в мой источник
  • отредактируйте его, чтобы получить только имена
  • сделать макрос, чтобы изменить имя на предложение case в вопросе, хотя обычно в одной строке: case foo: return "foo";
  • добавить переключатель, стандартный и другой синтаксис, чтобы сделать его легальным
1 голос
/ 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 пример использования.

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

Следующий скрипт ruby ​​пытается проанализировать заголовки и создает требуемые источники вместе с исходными заголовками.

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

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

Допустим, у вас есть заголовок toto / a.h, содержащий определения для перечислений MyEnum и MyEnum2. Скрипт будет строить:

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

Более надежные решения будут:

  • Сборка всех источников, определяющих перечисления и их операции, из другого источника. Это означает, что вы будете определять свои перечисления в XML / YML / любом другом файле, который гораздо проще анализировать, чем в C / C ++.
  • Используйте настоящий компилятор, такой как предложенный Avdi.
  • Использовать макросы препроцессора с шаблонами или без них.
1 голос
/ 05 октября 2018

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

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"
0 голосов
/ 07 марта 2013

Не так давно я сделал несколько хитростей, чтобы перечисления правильно отображались в QComboBox и чтобы определения перечислений и строковых представлений были одним оператором

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if(it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

Теперь у вас есть enumeration::enum_singleton<your_enum>::instance() возможность конвертировать перечисления в строки. Если вы замените kv_storage_t на boost::bimap, вы также сможете выполнить обратное преобразование. Общий базовый класс для конвертера был введен для хранения его в объекте Qt, потому что объекты Qt не могли быть шаблонами

Предыдущее появление

0 голосов
/ 10 июля 2018

Я столкнулся с этим вопросом, когда искал решение моей собственной проблемы для печати «слов» перечисления в C ++. Я вернулся, чтобы предложить простое решение, которое отвечает на поставленный вопрос в том виде, в котором оно сформулировано. Все, что требуется, это «отразить» список перечисления вектором.

enum class genre { Fiction, NonFiction, Periodical, Biography, Children };

vector<string>genre_tbl { "Fiction", "NonFiction", "Periodical", "Biography", "Children" };

Поскольку перечисленное выше перечисление будет делать следующее по умолчанию;

Fiction = 0
NonFiction = 1
Periodical = 2
Biography = 3
Children = 4

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

string s1 = genre_tbl[int(genre::fiction)];

Для моей задачи я создал пользовательский класс Book с членом Gen типа type genre. Программа должна была иметь возможность печатать жанр как слово.

class book {...};
ostream& operator<<(ostream& os, genre g) { return os << genre_tbl[int(g)]; }

book b1;
b1.Gen = genre(0)
cout << b1.Gen;

В этом случае «Fiction» будет печататься на экране.

0 голосов
/ 18 июля 2017

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

Вот хороший пример использования:

enum log_level {INFO, WARNING, ERROR};
...
void logger::write(const std::string log, const log_level l) {
    ...
    std::string s = (l == INFO) ? "INFO" : 
                    (l == WARNING) ? "WARNING" : 
                    (l == ERROR) ? "ERROR" : "UNKNOWN";
    ...
}
...

Конечно, это просто еще один оператор switch / if, но это однострочный оператор. И как краткость против простоты, это встречается где-то в середине. Как константное выражение, его также легко превратить в встроенную функцию.

0 голосов
/ 14 апреля 2017

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

Пример:

int main () {

  VERB a = VERB::GET;
  VERB b = VERB::GET;
  VERB c = VERB::POST;
  VERB d = VERB::PUT;
  VERB e = VERB::DELETE;


  std::cout << a.toString() << std::endl;

  std::cout << a << std::endl;

  if ( a == VERB::GET ) {
    std::cout << "yes" << std::endl;
  }

  if ( a == b ) {
    std::cout << "yes" << std::endl;
  }

  if ( a != c ) {
    std::cout << "no" << std::endl;
  }

}

VERB класс:

// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {

private:

  // private constants
  enum Verb {GET_=0, POST_, PUT_, DELETE_};

  // private string values
  static const std::string theStrings[];

  // private value
  const Verb value;
  const std::string text;

  // private constructor
  VERB (Verb v) :
  value(v), text (theStrings[v])
  {
    // std::cout << " constructor \n";
  }

public:

  operator const char * ()  const { return text.c_str(); }

  operator const std::string ()  const { return text; }

  const std::string toString () const { return text; }

  bool operator == (const VERB & other) const { return (*this).value == other.value; }

  bool operator != (const VERB & other) const { return ! ( (*this) == other); }

  // ---

  static const VERB GET;
  static const VERB POST;
  static const VERB PUT;
  static const VERB DELETE;

};

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};

const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file
0 голосов
/ 29 июля 2015

Проверьте это сообщение:

Реализация класса C ++ Enums

содержит реализацию класса перечисления c ++.

0 голосов
/ 25 апреля 2014
#include <iostream>
#include <map>
#define IDMAP(x) (x,#x)

std::map<int , std::string> enToStr;
class mapEnumtoString
{
public:
    mapEnumtoString(){  }
    mapEnumtoString& operator()(int i,std::string str)
    {
        enToStr[i] = str;
        return *this;
    }
public:
   std::string operator [] (int i)
    {
        return enToStr[i];
    }

};
mapEnumtoString k;
mapEnumtoString& init()
{
    return k;
}

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...