C ++ numaralandırmalarını dizelerle kolayca eşleme


119

Kullandığım bazı kitaplık başlık dosyalarında bir dizi enum türü var ve enum değerlerini kullanıcı dizelerine dönüştürmenin bir yolunu istiyorum - ve bunun tersi de geçerlidir.

RTTI bunu benim için yapmayacak, çünkü 'kullanıcı dizilerinin' numaralandırmalardan biraz daha okunaklı olması gerekiyor.

Bir kaba kuvvet çözümü bunun gibi bir dizi işlev olabilir, ancak bunun biraz fazla C benzeri olduğunu düşünüyorum.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

Şablonları kullanarak zarif bir çözüm olduğuna dair içimden bir his var, ancak henüz kafamı tam olarak anlayamıyorum.

GÜNCELLEME: Önerileriniz için teşekkürler - Numaraların üçüncü taraf bir kitaplık başlığında tanımlandığını açıkça belirtmeliydim, bu yüzden tanımlarını değiştirmek istemiyorum.

Şimdi içimden gelen his, şablonlardan kaçınmak ve şöyle bir şey yapmak:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

Yanıtlar:


60

Eğer dizeleri olarak enum isimlerini kendileri isterseniz, bkz Bu yayını . Aksi takdirde, a std::map<MyEnum, char const*>iyi çalışır. (Dize değişmezlerinizi haritadaki std :: dizelere kopyalamanın anlamı yok)

Ekstra sözdizimsel şeker için, işte bir map_init sınıfının nasıl yazılacağı. Amaç izin vermektir

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

İşlev template <typename T> map_init(T&), bir map_init_helper<T>. map_init_helper<T>bir T & saklar ve önemsiz olanı tanımlar map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Dönen *thisgelen operator()bir zincirleme sağlar operator()gibi, operator<<ilgili std::ostreams)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

İşlev ve yardımcı sınıf şablonlu olduğundan, bunları herhangi bir harita veya harita benzeri yapı için kullanabilirsiniz. Yani, girişler de ekleyebilirstd::unordered_map

Bu yardımcıları yazmaktan hoşlanmıyorsanız, boost :: assign aynı işlevselliği kutudan çıkarır.


Başka bir soruya başvurmakta haklısınız. İnsanlar gönderi yayınlamadan önce "ilgili sorulara" bakmalı ...
xtofl

2
@xtofl: Burada gösterilen "ilgili sorular", soruyu gönderdiğimde listelenen ilgili sorulardan tamamen farklı!
Roddy

@MSalters, std :: map, uygulamayı idare etmenin yararlı bir yoludur, ancak standart kodu azaltmanın gerektirebileceği bazı yollar arıyorum.
Roddy

@MSalters, [] operatörü için birden fazla argümanı kabul edebilmek güzel olurdu. ama ne yazık ki bunu kimse yapamaz. x [a, b] x [b] olarak değerlendirilir. (a, b) ifadesi virgül operatörünü kullanır. bu nedenle kodunuzda ["A"] ["B"] ["C"] ile eşdeğerdir. [eValue1] ["A"] [eValu ..
Johannes Schaub - litb

işlev çağrısı operatörü de iyi bir aday olabilir: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... bu durumda boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb

31

MSalters çözümü iyi bir çözümdür ancak temelde yeniden uygular boost::assign::map_list_of. Artışınız varsa, doğrudan kullanabilirsiniz:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

EeeToString'in bir sınıfın veri üyesi olduğu durumlarda bunu nasıl kullanırsınız? "Hata: veri üyesi başlatmaya izin verilmiyor"
Kullanıcı

@User: Sınıf veri üyeleri, genellikle başlatıcı listesinde oluşturucularda başlatılır.
MSalters

Bunu tüm numaralandırmalar için çalıştırmanın bir yolu var mı? Birden çok numaralandırma bildirimim var ve haritanın yalnızca eeesizin durumunuzda yazım için çalışmasını istemiyorum .
Justin Liang

Ben bir şablon kullanarak çalıştı ama sonra var ve hata: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang

4
Aslında bu cevap büyük ölçüde C ++ 11 ile geçerliliğini yitirmiştir.
Alastair

19

Bir formu diğerinden otomatik olarak oluşturun.

Kaynak:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Oluşturulan:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Enum değerleri büyükse, oluşturulan bir form unordered_map <> veya Constantin tarafından önerildiği gibi şablonlar kullanabilir.

Kaynak:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Oluşturulan:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Misal:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

En hızlı olmasına rağmen @MSalters kadar kolay değildir.
kenny

2
Bir metin dosyasından dizelerin bir listesini okumak ve derleme zamanında statik char ile bir .h dosyası oluşturmak için biraz perl / python'a sahipseniz. = "Program yazmak için programlar yazın"
Martin Beckett

@mgb: perl / python, herhangi bir dildeki hemen hemen her şablon motorunun yapacağı tek seçenek değildir (bu durumda, bir şablondan her iki formu da üretmektedir).
jfs

@jf. Evet, önemli olan nokta otomatik olarak derleme zamanında statik veri tabloları oluşturmaktı. Muhtemelen aptal bir statik dizi oluşturmayı tercih ederim.
Martin Beckett 05

Derleme sırasında Eyalet bilinmiyorsa bu işe yarar mı? Bunun olmayacağından oldukça eminim - teoride derleyicinin enum2str şablonunu enum'un tüm olası değerleriyle somutlaştırması gerekecekti, ki bunu gcc'nin (en azından) yapmayacağından oldukça eminim.
Alastair

11

Bunu StackOverflow'da başka bir yerde yanıtladığımı hatırlıyorum. Burada tekrar ediyorum. Temel olarak, değişken makrolara dayalı bir çözümdür ve kullanımı oldukça kolaydır:

#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;} 

Kodunuzda kullanmak için şunları yapmanız yeterlidir:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

1
Sadece pre c ++ 11 üzerinde çalışmak için enum sınıf bildirimini enum olarak değiştirin.
Debdatta Basu

1
Haklısın, işe yarıyor (auto da sadece c ++ 11). Güzel çözüm! Bazı numaralandırmalar için de bir değer belirleyebilseydiniz mükemmel olurdu
jamk

Sanırım boost'ta böyle bir şey gördüm
Sergei

10

X-makroları kullanmanın bir karışımının en iyi çözüm olduğunu ve aşağıdaki şablon işlevlerini öneriyorum :

Marcinkoziukmyopenidcom'u ödünç almak ve uzatmak için

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
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

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

Enum dize dizisi tanımını genel yapmanın bir yolu var mı? (Bir makro içinde bir X-Makroyu nasıl kullanacağımı bilmiyorum ve şablonu kolayca kullanamıyorum)
Jonathan

5

Aşağıda yeniden ürettiğim bu çözümü kullanıyorum :

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

1
Bu temel X Makrolarıdır ve bunu önermek için burada ilk cevabın bu olduğuna şaşırdım! +1
Orbit'te Hafiflik Yarışları

4

MyEnum Değişkenlerin dize temsillerini almak istiyorsanız , şablonlar onu kesmez. Şablon, derleme sırasında bilinen integral değerlerde özelleştirilebilir.

Ancak, istediğiniz buysa, deneyin:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Bu ayrıntılıdır, ancak söz konusu olduğunda yaptığınız gibi hataları yakalayacaktır - case VAL1sizinki kopyalanmıştır.


Aslında yöntem adı () gerekli değildir. Cevabımı gör.
jfs

3

Kabul etmek istediğim bu konuyu araştırmak için daha fazla zaman harcadım. Neyse ki vahşi ortamda harika açık kaynaklı çözümler var.

Bunlar, yeterince iyi bilinmese bile (henüz) iki harika yaklaşımdır.

wise_enum

  • C ++ 11/14/17 için bağımsız akıllı enum kitaplığı. C ++ 'da akıllı bir enum sınıfından bekleyebileceğiniz tüm standart işlevleri destekler.
  • Sınırlamalar: en az C ++ 11 gerektirir.

Daha İyi Numaralandırmalar

  • Yansıtıcı derleme zamanı numaralandırma kitaplığı, temiz sözdizimi ile, tek bir başlık dosyasında ve bağımlılıkları olmadan.
  • Sınırlamalar: Makrolara dayanır, bir sınıf içinde kullanılamaz.

2

Bir m haritasına sahip olmak cazip gelir ve bunu enum'a yerleştiririm.

m [MyEnum.VAL1] = "Değer 1" ile kurulum;

ve hepsi yapılır.


2

Başkalarından gelen kodu hata ayıklamak / analiz etmek için birkaç kez bu işlevselliğe ihtiyaç duydum. Bunun için, birkaç aşırı yüklenmiş toStringyöntemle bir sınıf oluşturan bir Perl betiği yazdım . Her toStringyöntem Enumbir argüman olarak alır ve geri döner const char*.

Elbette, komut dosyası numaralandırmalar için C ++ 'yı ayrıştırmaz, ancak sembol tablosu oluşturmak için ctags kullanır.

Perl betiği burada: http://heinitz-it.de/download/enum2string/enum2string.pl.html


2

Cevaplarınız bana bazı makrolar yazmam için ilham verdi. Gereksinimlerim şunlardı:

  1. numaralandırmanın her bir değerini yalnızca bir kez yazın, böylece sürdürülecek çift liste kalmaz

  2. enum değerlerini daha sonra #included olacak şekilde ayrı bir dosyada tutmayın, böylece istediğim yere yazabilirim

  3. numaralandırmanın kendisini değiştirmeyin, yine de enum türünün tanımlanmasını istiyorum, ancak buna ek olarak her enum adını karşılık gelen dizeyle eşleyebilmek istiyorum (eski kodu etkilememek için)

  4. arama hızlı olmalıdır, bu nedenle bu büyük numaralandırmalar için tercihen geçiş durumu olmamalıdır

Bu kod, bazı değerlerle klasik bir enum oluşturur. Ek olarak, her bir enum değerini kendi adına eşleyen std :: map olarak oluşturur (yani map [E_SUNDAY] = "E_SUNDAY", vb.)

Tamam, işte şimdi kod:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // bu, bunu yapmanız gerektiğinde eklemek istediğiniz dosyadır, içindeki makroları kullanacaksınız:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // bu, özel bir enum oluşturmak için nasıl kullanılacağına dair bir örnektir:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Şerefe.


2

Burada << ve >> akış operatörlerini enum'a otomatik olarak tek satırlı bir makro komutuyla alma girişimi ...

Tanımlar:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Kullanımı:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Yine de bu planın sınırlamalarından emin değilim ... yorumlarınızı bekliyoruz!


1

başlıkta:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

.cpp dosyasında:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Uyarı: Kötü dizi indeksiyle uğraşmayın. :) Ancak dizeyi diziden almadan önce numaralandırmayı doğrulamak için kolayca bir işlev ekleyebilirsiniz.


Gerçekten de DRY-SPOT olmayan bir çözüm.
xtofl

artık KURU'dan bahsettiğinize göre. başka bir girdi dosyasından otomatik olarak oluşturulan .h ve .cpp dosyası. Daha iyi çözümler görmeyi çok isterim (gereksiz karmaşıklığa başvurmayan)
Moogs

1

Makroları kullanarak bu olası zarif çözümü göstermek istedim. Bu sorunu çözmez ama bence sorun hakkında yeniden düşünmenin iyi bir yolu.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- DÜZENLE ----

Biraz internet araştırması ve bazı kendi deneyimlerimden sonra şu çözüme ulaştım:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Sadece göndermek istedim, belki birisi bu çözümü faydalı bulabilir. Şablon sınıflarına gerek yoktur, c ++ 11'e gerek yoktur ve güçlendirmeye gerek yoktur, bu nedenle bu basit C için de kullanılabilir.

---- DÜZENLE2 ---

bilgi tablosu 2'den fazla numaralandırma (derleyici problemi) kullanıldığında bazı problemler üretebilir. Aşağıdaki geçici çözüm işe yaradı:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Yukarıdaki basit çözümüm. Bunun bir yararı, mesaj dizisinin boyutunu kontrol eden 'NUM', ayrıca sınır dışı erişimi de engelliyor (akıllıca kullanırsanız).

Dizeyi almak için bir işlev de tanımlayabilirsiniz:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Çözümüme ek olarak, aşağıdakini oldukça ilginç buldum. Genellikle yukarıdakinin senkronizasyon problemini çözdü.

Burada slaytlar: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Kodu buraya: https://github.com/arunksaha/enum_to_string


1

Partiye geç kaldığımı biliyorum ama bu sayfayı ziyarete gelen diğer herkes için bunu deneyebilirsiniz, oradaki her şeyden daha kolay ve daha mantıklı:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Dizeleri kullanmayı ve numaralandırmayı hiç kullanmamayı mı öneriyorsunuz? Bu gerçekten sorunu çözmez.
Roddy

0

Geçenlerde bir satıcı kitaplığı (Fincad) ile aynı sorunu yaşadım. Neyse ki, satıcı tüm numaralandırmalar için xml belge sağladı. Her bir numaralandırma türü için bir eşleme oluşturmayı ve her bir enum için bir arama işlevi sağladım. Bu teknik aynı zamanda numaralandırma aralığı dışında bir aramaya müdahale etmenize de izin verir.

Eminim swig sizin için benzer bir şey yapabilir, ancak Ruby'de yazılmış kod oluşturma araçlarını sağlamaktan memnuniyet duyarım.

İşte kodun bir örneği:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Görünüşe göre diğer tarafa gitmek istiyorsunuz (dizeden enum'a değil, enum'dan dizeye), ancak bunu tersine çevirmek önemsiz olmalı.

-Whit


1
a) Başka biri bunu kesinlikle okunamaz buluyor mu? Birkaç typedef ve bildirimlerin kullanılması okunabilirliği büyük ölçüde artıracaktır. b) yerel statik bildirimler iş parçacığı açısından güvenli değildir. c) char * yerine const dizesi kullanın, d) atılan istisnada bulunamayan değeri dahil etmeye ne dersiniz?
Alastair

0

Aşağıdaki söz diziminin size uygun olup olmadığına bakın:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Varsa, şu makaleye göz atmak isteyebilirsiniz:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

Bu doğru eski karmaşa, SO'dan gelen bit ve kesiklere dayanan çabam. For_each, 20'den fazla enum değerini desteklemek için genişletilmelidir. Visual Studio 2019, clang ve gcc'de test etti. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

aşağıdaki kodu üreten

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Dünyanın en çok kullanılan programlama dillerinden birinde bunu yapmak için önişlemci ile birlikte atlamak zorunda olduğunuz çemberler ne kadar utanç verici ...


0

Belirlenmiş dizi başlatıcıları kullanarak, dize diziniz numaralandırmadaki öğelerin sırasından bağımsızdır:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.