มีวิธีง่ายๆในการแปลง C ++ enum เป็นสตริงหรือไม่?


123

สมมติว่าเรามีชื่อ enums:

enum MyEnum {
      FOO,
      BAR = 0x50
};

สิ่งที่ฉัน googled คือสคริปต์ (ภาษาใดก็ได้) ที่สแกนส่วนหัวทั้งหมดในโปรเจ็กต์ของฉันและสร้างส่วนหัวด้วยหนึ่งฟังก์ชันต่อ enum

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

gotcha นั้นมาพร้อมกับ enums typedefed และ enums สไตล์ C ที่ไม่มีชื่อ มีใครรู้เรื่องนี้บ้างไหม?

แก้ไข: โซลูชันไม่ควรแก้ไขแหล่งที่มาของฉันยกเว้นฟังก์ชันที่สร้างขึ้น enums อยู่ใน API ดังนั้นการใช้โซลูชันที่เสนอจนถึงตอนนี้จึงไม่ใช่ทางเลือก


คำตอบเกี่ยวกับโรงงานที่ใช้มาโครถูกย้ายไปที่stackoverflow.com/questions/147267/… - หลังจากที่คำถามได้รับการอัปเดตคำถามนี้ไม่เกี่ยวข้องอีกต่อไป
สุมา

คำตอบ:


48

คุณอาจต้องการที่จะตรวจสอบ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 และสร้างรหัสที่คุณต้องการ


ยอดเยี่ยม ใช้งานได้อย่างมีเสน่ห์ด้วยสคริปต์ Python ที่เรียบง่าย ขอบคุณ
Edu Felipe

6
+1, GCCXML ดูดีมาก! (แม้ว่าฉันเกือบจะ -1 ในขณะที่ฉันอ่านผิดในตอนแรกเป็นข้อเสนอแนะในการใช้ไวยากรณ์ XML verbose ด้านบนเพื่อเข้ารหัส enum ของคุณซึ่งเป็นวิธีแก้ปัญหาที่เน้นย้ำถึงการทำงานมากเกินไป!)
j_random_hacker

1
การเปลี่ยนแปลงใด ๆ ที่คุณสามารถโพสต์สคริปต์ python ได้หรือไม่
phillipwei

74

X-macros เป็นทางออกที่ดีที่สุด ตัวอย่าง:

#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.

11
ดีแม้ว่าฉันจะไม่ชอบไฟล์พิเศษ
Ronny Brendel

2
ตรวจสอบให้แน่ใจว่ากระบวนการสร้างของคุณไม่ได้นำหน้า #pragma (ครั้งเดียว) ก่อนทุกไฟล์รวม ...
xtofl

24
ฉันไม่แน่ใจเกี่ยวกับวิธีแก้ปัญหาที่ "ดีที่สุด"!
Lightness Races ใน Orbit

2
โซลูชันนี้เหนือกว่าเคสสวิตช์หรืออาร์เรย์ใด ๆ อย่างมากเนื่องจากไม่ซ้ำชื่อทำให้ง่ายต่อการเปลี่ยนการแจงนับ
Julien Guertault

2
@ ikku100 คุณไม่ถูกต้องเกี่ยวกับ#define X(a, b) #b. สิ่งนี้จำเป็นก็ต่อเมื่อคำจำกัดความมีลักษณะเช่นนี้X(Red, red)แทนที่จะเป็นคำจำกัดความที่แสดงในคำตอบX(Red, "red")
เรียนรู้

43

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

ฉันชอบวิธีนี้ จะชัดเจนกว่าถ้า SOME_UNION และ MAKE_UNION ถูกเรียกว่า SOME_ENUM และ MAKE_ENUM
Bruno Martinez

นี่เป็นทางออกที่ยอดเยี่ยม ฉันมีตัวจัดการทรัพยากร C ++ ที่ดูแลรักษาได้มากที่สุดเท่าที่ฉันเคยจัดการมา
DCurro

ฉันต้องขอบคุณสำหรับวิธีแก้ปัญหาง่ายๆนี้ :-) - แม้ว่าฉันจะแก้ไขเล็กน้อยเพื่อให้MetaSyntacticVariableNames[]เป็นส่วนหนึ่งของการประกาศคลาสโดยสร้างวิธีการstatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK

คำตอบสุดวิเศษ! ฉันทำให้มันง่ายขึ้นโดยการจัดกลุ่ม MAKE_ENUM และ MAKE_STRINGS เป็นมาโครเดียวทำให้กระบวนการทั้งหมดง่ายขึ้น ฉันได้เพิ่มคำตอบในชุดข้อความนี้ด้วยรหัสนั้นหากใครสนใจ
Francois Bertrand

35

สิ่งที่ฉันมักจะทำคือสร้างอาร์เรย์ 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);

จะพิมพ์ "สีแดง" เป็น stdout น่าเสียดายที่มันใช้ไม่ได้กับตัวแปร (เนื่องจากคุณจะได้รับชื่อตัวแปรที่พิมพ์ออกมา)


ข้อแม้สุดท้าย (ใช้ไม่ได้กับตัวแปร) เป็นข้อเสียเปรียบใหญ่ แต่ก็ +1 อยู่ดี
chappjc

3
ใช้ได้เฉพาะในกรณีที่คุณไม่ได้ตั้งค่าตัวเลขพิเศษให้กับรายการ enum
kyb

11

ฉันมีมาโครที่ใช้งานง่ายอย่างไม่น่าเชื่อซึ่งทำได้ในแบบแห้งสนิท มันเกี่ยวข้องกับมาโครที่หลากหลายและเวทมนตร์ในการแยกวิเคราะห์ง่ายๆ ที่นี่:

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

1
ความคิดที่ดีในการใช้ enum ที่พิมพ์อย่างรุนแรง (enum class) นี่คือตัวอย่าง: cpp.sh/4ife
chappjc

สิ่งนี้ใช้ได้กับการแจงนับ / สัญลักษณ์ที่กำหนดจากภายนอกหรือไม่ ตัวอย่างเช่น OS ที่กำหนดหรือสัญลักษณ์ที่กำหนดไลบรารีที่มีช่องว่างในการกำหนดหมายเลข?
Jason Harrison

ดีมาก แต่ไม่สามารถรวบรวมได้หากใส่ไว้ในชั้นเรียน (ฉันไม่สามารถหาสาเหตุได้)
AlwaysLearning

ฉันไม่สามารถรวบรวมสิ่งนี้ใน VS2015 ได้ ฉันได้รับคำเตือนและข้อผิดพลาด: Warning: multi-line comment [-Wcomment] #define MAKE_ENUM (name, ... ) enum class name { VA_ARGS , __COUNT} error: stray '#' in program std *: string enumName = #name
Craig เข้า

8

QT สามารถดึงสิ่งนั้น (ขอบคุณคอมไพเลอร์ meta object):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

ใน Qt ทุกคลาสที่มีมาโคร Q_OBJECT จะมีสมาชิกแบบคงที่ "staticMetaObject" ของประเภท QMetaObject โดยอัตโนมัติ จากนั้นคุณสามารถค้นหาสิ่งดีๆมากมายเช่นคุณสมบัติสัญญาณสล็อตและ enums ที่แท้จริง

แหล่ง


7

สามารถทำได้ใน 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;
}

1
สิ่งนี้ไม่ตอบคำถามของ OP: เขากำลังมองหาวิธีสร้างฟังก์ชันโดยอัตโนมัติเพื่อส่งคืนชื่อสมาชิกของ enum เป็นสตริง
Spooky

7

วันนี้ฉันเพิ่งประดิษฐ์วงล้อนี้ขึ้นมาใหม่และคิดว่าจะแบ่งปัน

การใช้งานนี้ไม่ต้องการการเปลี่ยนแปลงใด ๆ กับโค้ดที่กำหนดค่าคงที่ซึ่งอาจเป็นการแจงนับหรือ#defines หรือสิ่งอื่นใดที่เปลี่ยนเป็นจำนวนเต็ม - ในกรณีของฉันฉันมีสัญลักษณ์ที่กำหนดในรูปของสัญลักษณ์อื่น ๆ นอกจากนี้ยังใช้ได้ดีกับค่าเบาบาง แม้จะอนุญาตหลายชื่อสำหรับค่าเดียวกันโดยส่งคืนชื่อแรกเสมอ ข้อเสียเพียงอย่างเดียวคือคุณต้องสร้างตารางของค่าคงที่ซึ่งอาจล้าสมัยเมื่อมีการเพิ่มรายการใหม่

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

ทางออกที่ดี. แต่สำหรับฉันฉันชอบswitch and caseเพราะมันเรียบง่ายและเข้าใจง่าย
Deqing

6

4
อันที่จริงสิ่งนี้ค่อนข้างไร้ประโยชน์เนื่องจากเมธอด stringify อยู่ในเวลาคอมไพล์และค่อนข้างตรงตามตัวอักษร หากคุณบอกว่ามีประเภท enum ที่เป็นปัญหาอยู่ภายในตัวแปรการพยายามทำให้ตัวแปรสตริงเป็นเพียงแค่ให้ชื่อตัวแปรไม่ใช่ชื่อประเภท enum
srcspider

5

สนใจดูจำนวนวิธี นี่คือสิ่งที่ฉันใช้เมื่อนานมาแล้ว:

ในไฟล์ 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;

ไม่ใช่โครงสร้าง แต่สะดวก

นี่เป็นอีกวิธีหนึ่งที่ใช้คุณสมบัติ 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;
}

1
มันถูกกฎหมายอย่างสมบูรณ์ ฉันทำมันตลอดเวลา
Jonathan Graehl

ทางออกที่ดี. นี่คือ c ++ ดังนั้นการใช้แผนที่ stl ก็โอเค
Adam Bruss

4
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

การใช้งาน:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);

1
ฉันชอบ API ของคุณ แต่น่าเสียดายที่ SmartEnum ของคุณไม่ได้สร้าง "ประเภท" enum MyEnum x = MyEnum::TWO;คุณไม่สามารถทำ ฉันได้โพสต์การแก้ไขชั้นเรียนของคุณเพื่อรองรับสิ่งนี้
Mark Lakata

4

โซลูชันมาโครของ Sumaนั้นดี คุณไม่จำเป็นต้องมีมาโครสองตัวที่แตกต่างกัน C ++ จะรวมส่วนหัวสองครั้งอย่างมีความสุข เพียงแค่ปล่อยยามรวม

คุณจะมี foobar.h กำหนดเพียง

ENUM(Foo, 1)
ENUM(Bar, 2)

และคุณจะรวมไว้ดังนี้:

#define ENUMFACTORY_ARGUMENT "foobar.h"
#include "enumfactory.h"

enumfactory.h จะทำ 2 #include ENUMFACTORY_ARGUMENTวินาที ในรอบแรกจะขยาย ENUM เหมือนของ Suma DECLARE_ENUM; ในรอบที่สอง ENUM ทำงานเช่นDEFINE_ENUM.

คุณสามารถใส่ enumfactory.h หลาย ๆ ครั้งได้เช่นกันตราบเท่าที่คุณส่งผ่าน # define ต่างกันสำหรับ ENUMFACTORY_ARGUMENT


ดูเหมือนว่า Suma ย้ายคำตอบที่นี่ คุณอาจต้องการรวมลิงค์ไว้ในคำตอบของคุณ ฉันพบความคิดเห็นโดยบังเอิญและไม่มีคำตอบซูมาสข้อนี้ค่อนข้างไม่มีจุดหมาย
idclev 463035818

3

โปรดทราบว่าฟังก์ชันการแปลงของคุณควรส่งคืนค่าconst char *

หากคุณสามารถใส่ enums ของคุณในไฟล์ส่วนหัวที่แยกจากกันได้คุณอาจทำสิ่งนี้กับมาโคร (โอ้มันจะน่าเกลียด):

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

โดยที่ enum_def.h มี:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

และ enum_conv.h มี:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

และในที่สุด colour.h มี:

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

และคุณสามารถใช้ฟังก์ชันการแปลงเป็น:

printf("%s", colour_to_string(colour::red));

สิ่งนี้น่าเกลียด แต่เป็นวิธีเดียว (ในระดับตัวประมวลผลก่อน) ที่ให้คุณกำหนด enum ได้ในที่เดียวในโค้ดของคุณ ดังนั้นรหัสของคุณจึงไม่มีแนวโน้มที่จะเกิดข้อผิดพลาดเนื่องจากการแก้ไข enum นิยาม enum ของคุณและฟังก์ชันการแปลงจะซิงค์กันเสมอ อย่างไรก็ตามฉันขอย้ำว่ามันน่าเกลียด :)


3

คำตอบอื่น: ในบางบริบทคุณควรกำหนดการแจงนับของคุณในรูปแบบที่ไม่ใช่รหัสเช่นไฟล์ CSV, YAML หรือ XML จากนั้นสร้างทั้งรหัสการแจงนับ C ++ และรหัสสตริงจากคำจำกัดความ วิธีนี้อาจใช้ได้จริงหรือไม่ก็ได้ในแอปพลิเคชันของคุณ แต่สิ่งที่ควรคำนึงถึง


3

นี่คือการแก้ไขคำตอบของ @ user3360260 มีคุณสมบัติใหม่ดังต่อไปนี้

  • MyEnum fromString(const string&) สนับสนุน
  • รวบรวมด้วย VisualStudio 2012
  • enum เป็นประเภท POD จริง (ไม่ใช่แค่การประกาศ const) ดังนั้นคุณสามารถกำหนดให้กับตัวแปรได้
  • เพิ่มคุณลักษณะ "ช่วง" ของ C ++ (ในรูปแบบของเวกเตอร์) เพื่ออนุญาตให้มีการทำซ้ำ "foreach" เหนือ enum

การใช้งาน:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

นี่คือรหัส

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

โปรดทราบว่าการแปลงเป็นStringนั้นมีการค้นหาอย่างรวดเร็วในขณะที่การแปลงจากสตริงเป็นการค้นหาเชิงเส้นที่ช้า แต่สตริงมีราคาแพงมาก (และไฟล์ที่เกี่ยวข้อง) ฉันไม่รู้สึกว่าจำเป็นต้องปรับให้เหมาะสมหรือใช้ bimap


คุณและ user3360260 มีทางออกที่ดี ทำไมไม่มีมัลติแมปแทนล่ะ?
Vincent

3

นี่คือโซลูชันไฟล์เดียว (อ้างอิงจากคำตอบที่สวยงามโดย @Marcin:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

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

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

2

ฉันทำสิ่งนี้ด้วยคลาส enum wrapper แบบเคียงข้างกันซึ่งสร้างขึ้นด้วยมาโคร มีข้อดีหลายประการ:

  • สามารถสร้างมันสำหรับ enums ที่ฉันไม่ได้กำหนด (เช่น: OS Platform header enums)
  • สามารถรวมการตรวจสอบช่วงเข้ากับคลาส Wrapper
  • สามารถจัดรูปแบบ "อย่างชาญฉลาด" ด้วย bit field enums

ข้อเสียแน่นอนคือฉันต้องทำซ้ำค่า enum ในคลาสฟอร์แมตเตอร์และฉันไม่มีสคริปต์ใด ๆ ในการสร้าง นอกเหนือจากนั้นดูเหมือนว่าจะทำงานได้ดี

นี่คือตัวอย่างของ enum จาก codebase ของฉันซึ่งมีโค้ดกรอบงานทั้งหมดที่ใช้มาโครและเทมเพลต แต่คุณจะได้รับแนวคิด:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

จากนั้นแนวคิดก็คือแทนที่จะใช้ EHelpLocation คุณใช้ SEHelpLocation ทุกอย่างทำงานเหมือนกัน แต่คุณจะได้รับการตรวจสอบช่วงและวิธี 'Format ()' ในตัวแปร enum เอง หากคุณต้องการจัดรูปแบบค่าแบบสแตนด์อะโลนคุณสามารถใช้ CEnumFormatter_EHelpLocation :: FormatEnum (... )

หวังว่านี่จะเป็นประโยชน์ ฉันตระหนักดีว่าสิ่งนี้ไม่ได้ตอบคำถามดั้งเดิมเกี่ยวกับสคริปต์เพื่อสร้างคลาสอื่น ๆ จริง ๆ แต่ฉันหวังว่าโครงสร้างจะช่วยให้คนที่พยายามแก้ปัญหาเดียวกันหรือเขียนสคริปต์ดังกล่าว


2

เป็นซอฟต์แวร์ที่ยังไม่เปิดตัว แต่ดูเหมือนว่า BOOST_ENUM จาก Frank Laub จะพอดีกับใบเรียกเก็บเงินนี้ ส่วนที่ฉันชอบคือคุณสามารถกำหนด enum ภายในขอบเขตของคลาสซึ่ง enums ที่ใช้มาโครส่วนใหญ่มักจะไม่ยอมให้คุณทำ มันอยู่ใน Boost Vault ที่: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& มันไม่เห็นการพัฒนาใด ๆ เลยตั้งแต่ปี 2006 ดังนั้นฉันจึงไม่ รู้ว่ามันรวบรวมกับ Boost รุ่นใหม่ได้ดีเพียงใด ดูตัวอย่างการใช้งานใน libs / test


2

นี่เป็นวิธีแก้ปัญหาของฉันด้วย BOOST:

#include <boost/preprocessor.hpp>

#define X_STR_ENUM_TOSTRING_CASE(r, data, elem)                                 \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if(data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if(v=="")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

ในการสร้าง enum ประกาศ:

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

สำหรับการแปลง:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);

2

ฉันต้องการโพสต์สิ่งนี้เผื่อว่ามีคนเห็นว่ามีประโยชน์

ในกรณีของฉันฉันแค่ต้องสร้างToString()และFromString()ฟังก์ชันสำหรับ C ++ 11 enum เดียวจากไฟล์.hppไฟล์

ฉันเขียนสคริปต์ python ที่แยกวิเคราะห์ไฟล์ส่วนหัวที่มีรายการ enum และสร้างฟังก์ชันในไฟล์ .cppไฟล์

คุณสามารถเพิ่มสคริปต์นี้ใน CMakeLists.txt ด้วยexecute_processหรือเป็นเหตุการณ์ก่อนสร้างใน Visual Studio .cppไฟล์จะถูกสร้างขึ้นโดยอัตโนมัติโดยไม่จำเป็นต้องปรับปรุงด้วยตนเองทุกครั้งที่มีรายการ enum ใหม่จะถูกเพิ่ม

generate_enum_strings.py

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

ตัวอย่าง:

ErrorCode.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

วิ่ง python generate_enum_strings.py ErrorCode.hpp

ผลลัพธ์:

ErrorCode.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(ErrorCode errorCode)
    {
        switch (errorCode)
        {
        case ErrorCode::OK:
            return "OK";
        case ErrorCode::OutOfSpace:
            return "OutOfSpace";
        case ErrorCode::ConnectionFailure:
            return "ConnectionFailure";
        case ErrorCode::InvalidJson:
            return "InvalidJson";
        case ErrorCode::DatabaseFailure:
            return "DatabaseFailure";
        case ErrorCode::HttpError:
            return "HttpError";
        case ErrorCode::FileSystemError:
            return "FileSystemError";
        case ErrorCode::FailedToEncrypt:
            return "FailedToEncrypt";
        case ErrorCode::FailedToDecrypt:
            return "FailedToDecrypt";
        case ErrorCode::EndOfFile:
            return "EndOfFile";
        case ErrorCode::FailedToOpenFileForRead:
            return "FailedToOpenFileForRead";
        case ErrorCode::FailedToOpenFileForWrite:
            return "FailedToOpenFileForWrite";
        case ErrorCode::FailedToLaunchProcess:
            return "FailedToLaunchProcess";
        case ErrorCode::Last:
            return "Last";
        default:
            return "Last";
        }
    }

    ErrorCode FromString(const std::string &errorCode)
    {
        if ("OK" == errorCode)
        {
            return ErrorCode::OK;
        }
        else if("OutOfSpace" == errorCode)
        {
            return ErrorCode::OutOfSpace;
        }
        else if("ConnectionFailure" == errorCode)
        {
            return ErrorCode::ConnectionFailure;
        }
        else if("InvalidJson" == errorCode)
        {
            return ErrorCode::InvalidJson;
        }
        else if("DatabaseFailure" == errorCode)
        {
            return ErrorCode::DatabaseFailure;
        }
        else if("HttpError" == errorCode)
        {
            return ErrorCode::HttpError;
        }
        else if("FileSystemError" == errorCode)
        {
            return ErrorCode::FileSystemError;
        }
        else if("FailedToEncrypt" == errorCode)
        {
            return ErrorCode::FailedToEncrypt;
        }
        else if("FailedToDecrypt" == errorCode)
        {
            return ErrorCode::FailedToDecrypt;
        }
        else if("EndOfFile" == errorCode)
        {
            return ErrorCode::EndOfFile;
        }
        else if("FailedToOpenFileForRead" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForRead;
        }
        else if("FailedToOpenFileForWrite" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForWrite;
        }
        else if("FailedToLaunchProcess" == errorCode)
        {
            return ErrorCode::FailedToLaunchProcess;
        }
        else if("Last" == errorCode)
        {
            return ErrorCode::Last;
        }

        return ErrorCode::Last;
    }
}

1
นี่คือเครื่องกำเนิดไฟฟ้าออนไลน์: th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann

2

เพิ่มความเรียบง่ายในการใช้งานให้กับคำตอบที่ยอดเยี่ยมของ Jasper Bekkers :

ตั้งค่าครั้งเดียว:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

จากนั้นสำหรับการใช้งาน:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

2

คุณสามารถใช้ห้องสมุดสะท้อนเช่นไตร่ตรอง คุณลงทะเบียน enums จากนั้นคุณสามารถแปลงกลับไปกลับมาด้วย 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"

1

ปัญหาเกี่ยวกับคำตอบ 0 คือค่าเลขฐานสอง enum ไม่จำเป็นต้องเริ่มต้นที่ 0 และไม่จำเป็นต้องต่อเนื่องกัน

เมื่อฉันต้องการสิ่งนี้ฉันมักจะ:

  • ดึงนิยาม enum มาเป็นแหล่งที่มาของฉัน
  • แก้ไขเพื่อให้ได้เพียงชื่อ
  • ทำมาโครเพื่อเปลี่ยนชื่อเป็น case clause ในคำถามแม้ว่าโดยทั่วไปจะอยู่ในบรรทัดเดียว: case foo: return "foo";
  • เพิ่มสวิตช์ค่าเริ่มต้นและไวยากรณ์อื่น ๆ เพื่อให้ถูกกฎหมาย

1

สคริปต์ 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 / ah ซึ่งมีคำจำกัดความสำหรับ enums MyEnum และ MyEnum2 สคริปต์จะสร้าง:

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

โซลูชันที่มีประสิทธิภาพมากขึ้น ได้แก่ :

  • สร้างแหล่งที่มาทั้งหมดที่กำหนด enums และการดำเนินการจากแหล่งอื่น ซึ่งหมายความว่าคุณจะกำหนด enums ของคุณใน XML / YML / ไฟล์อะไรก็ได้ที่แยกวิเคราะห์ได้ง่ายกว่า C / C ++
  • ใช้คอมไพเลอร์จริงตามที่ Avdi แนะนำ
  • ใช้มาโครตัวประมวลผลล่วงหน้าที่มีหรือไม่มีเทมเพลต

0

นั่นเป็นวิธีเดียวที่สามารถทำได้ (อาร์เรย์ของสตริงก็ใช้ได้เช่นกัน)

ปัญหาคือเมื่อคอมไพล์โปรแกรม C แล้วค่าไบนารีของ enum จะถูกใช้ทั้งหมดและชื่อก็หายไป


0

นี่คือโปรแกรม CLI ที่ฉันเขียนขึ้นเพื่อแปลง enums เป็นสตริงได้อย่างง่ายดาย ใช้งานง่ายและใช้เวลาประมาณ 5 วินาทีในการทำ (รวมถึงเวลาในการ cd ไปยังไดเร็กทอรีที่มีโปรแกรมจากนั้นเรียกใช้งานโดยส่งผ่านไฟล์ที่มี enum)

ดาวน์โหลดที่นี่: http://www.mediafire.com/?nttignoozzz

หัวข้อสนทนาที่นี่: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

เรียกใช้โปรแกรมด้วยอาร์กิวเมนต์ "--help" เพื่อรับคำอธิบายวิธีการใช้งาน


คุณช่วยวางสิ่งนี้ไว้ในที่เก็บที่ไหนสักแห่งได้ไหม (github, google code หรือ bitbucket) และโพสต์ลิงค์ที่นี่แทนที่จะเป็น mediafire ฉันจะช่วยให้คนที่อยากเข้าใจมัน :)
Edu Felipe

0

เมื่อไม่นานมานี้ฉันได้ทำเคล็ดลับเพื่อให้ enums แสดงอย่างถูกต้องใน QComboBox และมีคำจำกัดความของ enum และการแสดงสตริงเป็นคำสั่งเดียว

#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()สามารถแปลง enums เป็นสตริงได้แล้ว หากคุณแทนที่kv_storage_tด้วยboost::bimapคุณจะสามารถทำการแปลงย้อนหลังได้ คลาสพื้นฐานทั่วไปสำหรับตัวแปลงถูกนำมาใช้เพื่อเก็บไว้ในอ็อบเจ็กต์ Qt เนื่องจากอ็อบเจ็กต์ Qt ไม่สามารถเป็นเทมเพลตได้

ลักษณะก่อนหน้า


0

เป็นตัวแปรให้ใช้ lib แบบง่าย> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

ในรหัส

#include <EnumString.h>

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

เพิ่มบรรทัด

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

ทำงานได้ดีถ้าค่าใน enum ไม่ได้ dublicate

ตัวอย่างการใช้งาน

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

และในทางกลับกัน

assert( EnumString< FORM >::To( f, str ) );

0

นี่คือความพยายามที่จะรับ << และ >> สตรีมโอเปอเรเตอร์บน enum โดยอัตโนมัติด้วยคำสั่งมาโครบรรทัดเดียวเท่านั้น ...

คำนิยาม:

#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]); \
    }

การใช้งาน:

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

ไม่แน่ใจเกี่ยวกับข้อ จำกัด ของโครงการนี้ว่า ... ยินดีต้อนรับความคิดเห็น!


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

2
กรุณาอธิบายว่าทำไมจึงเป็นคำตอบ
Alok
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.