هل هناك طريقة بسيطة لتحويل التعداد C++ إلى سلسلة؟

StackOverflow https://stackoverflow.com/questions/201593

  •  03-07-2019
  •  | 
  •  

سؤال

لنفترض أن لدينا بعض التعدادات المسماة:

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

المشكلة هي في الواقع مع التعدادات المكتوبة، والتعدادات ذات النمط C غير المسماة.هل يعرف أحد شيئًا عن هذا؟

يحرر:يجب ألا يقوم الحل بتعديل المصدر الخاص بي، باستثناء الوظائف التي تم إنشاؤها.التعدادات موجودة في واجهة برمجة التطبيقات (API)، لذا فإن استخدام الحلول المقترحة حتى الآن ليس خيارًا.

هل كانت مفيدة؟

المحلول

وأنت قد ترغب في التحقق من 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>

هل يمكن استخدام أي اللغة التي تفضلها لسحب التعداد وEnumValue العلامات وإنشاء التعليمات البرمجية المطلوب.

نصائح أخرى

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

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

وماذا أنا أميل إلى القيام به هو خلق مجموعة C مع أسماء في نفس النظام ومنصب القيم التعداد.

وعلى سبيل المثال.

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

وبعد ذلك يمكنك استخدام مجموعة في الأماكن التي تريد قيمة للقراءة الإنسان، على سبيل المثال

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

هل يمكن أن التجربة قليلا مع المشغل stringizing (انظر # في إشارة المعالج الخاص بك) من شأنها أن تفعل ما تريد، في بعض circumstances- على سبيل المثال:

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

وسيتم طباعة "الأحمر" لالمعياري. للأسف فإنه لن ينجح لمتغير (كما ستحصل على اسم المتغير طباعتها)

ولدي بسيطة للغاية لاستخدام الماكرو الذي يفعل ذلك بطريقة جاف تماما. أنها تنطوي على وحدات الماكرو variadic وبعض بسيطة السحر التوزيع. هنا المثل:

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

وQT قادرة على سحب هذا من (بفضل المترجم الكائن ميتا): <وأ href = "http://blog.qt.io/blog/2008/10/09/coding-tip-pretty-printing- تعداد القيم / "يختلط =" نوفولو noreferrer "> رابط

وأنا مجرد إعادة اختراع العجلة هذا اليوم، واعتقدت أن المشاركة فيها.

وهذا التطبيق لا <م> لا تتطلب أية تغييرات على التعليمات البرمجية التي تحدد الثوابت، والتي يمكن أن تكون التعدادات أو #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;
    }
}
#define stringify( name ) # name

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

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

مزيد من النقاش حول هذه الطريقة

قبل المعالج الحيل التوجيه للوافدين الجدد

والمثير للاهتمام أن نرى عدد من الطرق. هنا واحد اعتدت منذ فترة طويلة:

وفي 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;
}

ويمكن القيام بذلك في 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;
}
#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);

والجواب أخرى: في بعض السياقات، فمن المنطقي لتحديد تعداد الخاصة بك في شكل غير رمز، مثل CSV، YAML، أو ملف XML، ومن ثم إنشاء كل من C ++ كود التعداد ورمز لسلسلة من تعريف. وهذا النهج قد أو قد لا يكون عمليا في التطبيق الخاص بك، ولكن هذا شيء أن نأخذ في الاعتبار.

هذا تعديل لإجابة @ user3360260.لديها الميزات الجديدة التالية

  • MyEnum fromString(const string&) يدعم
  • يجمع مع VisualStudio 2012
  • التعداد هو نوع POD فعلي (وليس فقط إعلانات const)، لذا يمكنك تعيينه لمتغير.
  • تمت إضافة ميزة "النطاق" C++ (في شكل متجه) للسماح بتكرار "foreach" عبر التعداد

الاستخدام:

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

لاحظ أن التحويل إلى سلسلة هو بحث سريع، في حين أن التحويل من سلسلة هو بحث خطي بطيء.لكن السلاسل باهظة الثمن على أي حال (والملف IO المرتبط بها)، ولم أشعر بالحاجة إلى تحسين الصورة الثنائية أو استخدامها.

الحل الكلي سوما ل عبارة لطيفة. أنت لا تحتاج أن يكون اثنين في الكلية المختلفة، وإن كان. C ++ فيل تشمل بسعادة رأس مرتين. مجرد ترك خارجا تشمل الحراسة.

وهكذا كنت قد وfoobar.h تحديد فقط

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

وأنت إدراجه مثل هذا:

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

وenumfactory.h ستفعل 2 #include ENUMFACTORY_ARGUMENTs. في الجولة الأولى، فإنه يوسع التعداد مثل DECLARE_ENUM سوما؛ و في الجولة الثانية التعداد تعمل مثل DEFINE_ENUM.

وأنت يمكن أن تشمل enumfactory.h عدة مرات أيضا، طالما أنك تمر في مختلف # تعريف لENUMFACTORY_ARGUMENT

لاحظ أن وظيفة التحويل الخاصة بك يجب أن تُرجع بشكل مثالي ملف مقدار ثابت شار *.

إذا كان بإمكانك وضع التعدادات الخاصة بك في ملفات رأسية منفصلة، ​​فربما يمكنك القيام بشيء كهذا مع وحدات الماكرو (أوه، سيكون هذا قبيحًا):

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

وأخيرًا، يحتوي color.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 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()" في متغير التعداد نفسه.إذا كنت بحاجة إلى تنسيق قيمة مستقلة، فيمكنك استخدام CEnumFormatter_EHelpLocation::FormatEnum(...).

نأمل أن يكون هذا مفيدا.أدرك أن هذا أيضًا لا يعالج السؤال الأصلي حول البرنامج النصي لإنشاء فئة أخرى بالفعل، ولكن آمل أن يساعد الهيكل شخصًا يحاول حل نفس المشكلة، أو كتابة مثل هذا البرنامج النصي.

وهنا حل ملف واحد (على أساس الإجابة أنيقة من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;
}

وهذا هو بلدي حل مع 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());                        \
    }

لإنشاء التعداد، نعلن ما يلي:

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

لالتحويلات:

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

وإضافة المزيد من سهولة الاستخدام ل جاسبر بيكرز "رائعة الجواب :

وإعداد مرة واحدة:

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

مشكلة الإجابة 0 هي أن القيم الثنائية للتعداد لا تبدأ بالضرورة عند 0 وليست بالضرورة متجاورة.

عندما أحتاج إلى هذا، عادةً ما:

  • اسحب تعريف التعداد إلى مصدري
  • تحريره للحصول على الأسماء فقط
  • قم بإجراء ماكرو لتغيير الاسم إلى جملة الحالة في السؤال، على الرغم من أنه عادةً ما يكون على سطر واحد:حالة فو:إرجاع "فو" ؛
  • أضف المفتاح والصيغة الافتراضية وغيرها لجعلها قانونية

يحاول نص الياقوت التالي تحليل الرؤوس وإنشاء المصادر المطلوبة بجانب الرؤوس الأصلية.

#! /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.
  • استخدم وحدات ماكرو المعالج المسبق مع القوالب أو بدونها.

وانها تقف البرمجيات ولكن يبدو BOOST_ENUM من فرانك لوب يمكن أن تتلاءم مع القانون. الجزء يعجبني فيها هو أنه يمكنك تعريف التعداد في نطاق فئة التي معظم تتضمن التعدادات ماكرو تستند عادة لا تسمح لك أن تفعل. وهي تقع في المدفن دفعة في: HTTP : //www.boostpro.com/vault/index.php عمل = downloadfile واسم الملف = enum_rev4.6.zip والدليل = & لم يطلع عليها أي تطور منذ عام 2006 حتى أنا لا أعرف جيدا كيف يجمع مع الإصدارات دفعة جديدة. انظر تحت يبس / اختبار للحصول على مثال من الاستخدام.

أريد أن أنشر هذا في حال وجد شخص ما أنه مفيد.

في حالتي، أنا ببساطة بحاجة إلى توليد ToString() و FromString() وظائف لتعداد C++ 11 واحد من ملف واحد .hpp ملف.

لقد كتبت برنامجًا نصيًا بيثون يقوم بتوزيع ملف الرأس الذي يحتوي على عناصر التعداد ويقوم بإنشاء الوظائف في ملف جديد .cpp ملف.

يمكنك إضافة هذا البرنامج النصي في CMakeLists.txt باستخدام execute_process, أو كحدث ما قبل الإنشاء في Visual Studio.ال .cpp سيتم إنشاء الملف تلقائيًا، دون الحاجة إلى تحديثه يدويًا في كل مرة تتم فيها إضافة عنصر تعداد جديد.

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

هل يمكن استخدام مكتبة التفكير، مثل التأمل . قمت بتسجيل تتضمن التعدادات وبعد ذلك يمكنك تحويلها ذهابا وإيابا مع 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"

وهذا الى حد كبير الطريقة الوحيدة التي يمكن القيام به (مجموعة من سلسلة يمكن أن تعمل أيضا).

والمشكلة هي، مرة واحدة جمعت برنامج C، وقيمة ثنائية من التعداد هو كل ما يستخدم، وذهب الاسم.

وهنا هو برنامج CLI كتبت لتحويل تتضمن التعدادات بسهولة إلى سلاسل. من السهل الاستخدام، ويستغرق حوالي 5 ثوان لانجاز ذلك (بما في ذلك الوقت لمؤتمر نزع السلاح إلى الدليل الذي يحتوي على البرنامج، ثم تشغيله، ويمر عليه الملف الذي يحتوي على التعداد).

وتحميل هنا: http://www.mediafire.com/؟nttignoozzz

وموضوع مناقشة من هنا: HTTP: //cboard.cprogramming. كوم / مشاريع مهمة التوظيف / 127488-برنامج مجاني تقاسم ايم-convertenumtostrings.html

وتشغيل برنامج بالحجة "--help" للحصول على وصف كيفية استخدامها.

قبل

وليس ببعيد أدليت به بعض خدعة لديك تتضمن التعدادات عرض بشكل صحيح في 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، سوف تكون ايضا قادرة على القيام تحويل الوراء. وقدم الفئة الأساسية المشتركة لتحويل لتخزينه في الجسم كيو تي، لأن الأجسام كيو تي لا يمكن أن تكون قوالب

ظهور السابق

كمتغير، استخدم 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 FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

والعكس صحيح

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

وهنا محاولة للحصول على << >> ومشغلي البث المباشر على التعداد تلقائيا مع أمر ماكرو سطر واحد فقط ...

والتعاريف:

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

وغير متأكدة من القيود المفروضة على هذا المخطط على الرغم من ... التعليقات هي موضع ترحيب!

#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];
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top