طريقة سهلة استخدام المتغيرات من أنواع التعداد كما في سلسلة C ؟

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

  •  02-07-2019
  •  | 
  •  

سؤال

هذا ما أحاول القيام به:

typedef enum { ONE, TWO, THREE } Numbers;

أنا أحاول أن أكتب وظيفة التي من شأنها أن تفعل تبديل حالة مشابهة لما يلي:

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

بدلا من أن تحدد في كل حالة, هل هناك طريقة لضبط باستخدام enum متغير مثل أحاول فعله أعلاه ؟

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

المحلول

هناك المدمج في الحل.أسهل طريقة هي مع مجموعة من char* حيث التعداد int القيمة مؤشرات إلى سلسلة تحتوي على اسم وصفي من هذا التعداد.إذا كان لديك متفرق enum (لا تبدأ من 0 أو الثغرات في الترقيم) حيث بعض int تعيينات مرتفعة بما يكفي لجعل مجموعة رسم الخرائط على أساس عملي ثم يمكنك استخدام جدول تجزئة بدلا من ذلك.

نصائح أخرى

هذه التقنية من صنع شيء كل ج " و " معرف سلسلة ؟ يمكن استخدامها هنا.

كالعادة مع هذا المعالج الأشياء والكتابة فهم المعالج جزء يمكن أن يكون من الصعب ، ويشمل يمر وحدات الماكرو إلى وحدات الماكرو الأخرى و ينطوي على استخدام # و ## المشغلين ، ولكن استخدامه سهلا.أجد هذا النمط مفيد جدا لفترة طويلة enums ، حيث الحفاظ على نفس القائمة مرتين يمكن أن تكون مزعجة حقا.

مصنع رمز كتبته مرة واحدة فقط ، وعادة ما تكون مخبأة في الرأس:

enumFactory.h:

// expansion macro for enum value definition
#define ENUM_VALUE(name,assign) name assign,

// expansion macro for enum to string conversion
#define ENUM_CASE(name,assign) case name: return #name;

// expansion macro for string to enum conversion
#define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;

/// declare the access function and define enum values
#define DECLARE_ENUM(EnumType,ENUM_DEF) \
  enum EnumType { \
    ENUM_DEF(ENUM_VALUE) \
  }; \
  const char *GetString(EnumType dummy); \
  EnumType Get##EnumType##Value(const char *string); \

/// define the access function names
#define DEFINE_ENUM(EnumType,ENUM_DEF) \
  const char *GetString(EnumType value) \
  { \
    switch(value) \
    { \
      ENUM_DEF(ENUM_CASE) \
      default: return ""; /* handle input error */ \
    } \
  } \
  EnumType Get##EnumType##Value(const char *str) \
  { \
    ENUM_DEF(ENUM_STRCMP) \
    return (EnumType)0; /* handle input error */ \
  } \

مصنع المستعملة

someEnum.h:

#include "enumFactory.h"
#define SOME_ENUM(XX) \
    XX(FirstValue,) \
    XX(SecondValue,) \
    XX(SomeOtherValue,=50) \
    XX(OneMoreValue,=100) \

DECLARE_ENUM(SomeEnum,SOME_ENUM)

someEnum.cpp:

#include "someEnum.h"
DEFINE_ENUM(SomeEnum,SOME_ENUM)

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

مقارنة X-وحدات الماكرو باستخدام #include / #define / #صندوق الأمم المتحدة للديمقراطية

في حين أن هذا هو مماثل X-وحدات الماكرو ذكر آخرون ، وأعتقد أن هذا الحل هو أكثر أناقة في أنها لا تتطلب #undefing أي شيء ، والذي يسمح لك لإخفاء أكثر الأشياء تعقيدا في مصنع رأس الملف - ملف الرأس هو شيء كنت لا لمس على الإطلاق عندما كنت في حاجة إلى تعريف جديد التعداد ، ولذلك جديدة التعداد التعريف هو أقصر بكثير وأنظف.

// Define your enumeration like this (in say numbers.h);
ENUM_BEGIN( Numbers )
    ENUM(ONE),
    ENUM(TWO),
    ENUM(FOUR)
ENUM_END( Numbers )

// The macros are defined in a more fundamental .h file (say defs.h);
#define ENUM_BEGIN(typ) enum typ {
#define ENUM(nam) nam
#define ENUM_END(typ) };

// Now in one and only one .c file, redefine the ENUM macros and reinclude
//  the numbers.h file to build a string table
#undef ENUM_BEGIN
#undef ENUM
#undef ENUM_END
#define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
#define ENUM(nam) #nam
#define ENUM_END(typ) };
#undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
#include "numbers.h"

// Now you can do exactly what you want to do, with no retyping, and for any
//  number of enumerated types defined with the ENUM macro family
//  Your code follows;
char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
    } break;
    default:
      return 0; //no match
  return 1;
}

// Sweet no ? After being frustrated by this for years, I finally came up
//  with this solution for my most recent project and plan to reuse the idea
//  forever

بالتأكيد هناك طريقة للقيام بذلك -- استخدام X() وحدات الماكرو.استخدم وحدات الماكرو هذه ج المعالج لبناء enums ، المصفوفات و كتل التعليمات البرمجية من قائمة من مصدر البيانات.تحتاج فقط إلى إضافة عناصر جديدة إلى #تعريف تحتوي على X() الماكرو.بيان التبديل توسيع تلقائيا.

المثال الخاص بك يمكن أن تكون مكتوبة على النحو التالي:

 // Source data -- Enum, String
 #define X_NUMBERS \
    X(ONE,   "one") \
    X(TWO,   "two") \
    X(THREE, "three")

 ...

 // Use preprocessor to create the Enum
 typedef enum {
  #define X(Enum, String)       Enum,
   X_NUMBERS
  #undef X
 } Numbers;

 ...

 // Use Preprocessor to expand data into switch statement cases
 switch(num)
 {
 #define X(Enum, String) \
     case Enum:  strcpy(num_str, String); break;
 X_NUMBERS
 #undef X

     default: return 0; break;
 }
 return 1;

هناك طرق أكثر كفاءة (أيباستخدام X وحدات الماكرو لإنشاء صفيف سلسلة و التعداد مؤشر), ولكن هذا هو أبسط التجريبي.

أعلم أن لديك بضع الصلبة جيدة إجابات, ولكن هل تعرف عن # مشغل في ج المعالج?

فإنه يتيح لك القيام بذلك:

#define MACROSTR(k) #k

typedef enum {
    kZero,
    kOne,
    kTwo,
    kThree
} kConst;

static char *kConstStr[] = {
    MACROSTR(kZero),
    MACROSTR(kOne),
    MACROSTR(kTwo),
    MACROSTR(kThree)
};

static void kConstPrinter(kConst k)
{
    printf("%s", kConstStr[k]);
}

قبلة.كنت سوف تفعل كل أنواع أخرى التبديل/حالة الأشياء مع enums فلماذا الطباعة تكون مختلفة ؟ متناسين قضية في الطباعة الروتينية ليست صفقة ضخمة عندما كنت تنظر إلى أن هناك حوالي 100 أماكن أخرى يمكن أن ننسى القضية.فقط تجميع الجدار الذي يحذر من غير شاملة الحالة المباريات.لا استخدام "الافتراضي" لأن من شأنها أن تجعل التحول شاملة و كنت متعود على التحذيرات.بدلا من ذلك دعونا التبديل الخروج والتعامل مع الحالة الافتراضية مثل ذلك...

const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return "one";
    case TWO: return "two";
    }
    return "invalid";
}

C أو C++ لا توفر هذه الوظيفة ، على الرغم من أنني قد يحتاج إليها في كثير من الأحيان.

التعليمة البرمجية التالية يعمل, على الرغم من أنه من الأنسب عدم متفرق enums.

typedef enum { ONE, TWO, THREE } Numbers;
char *strNumbers[] = {"one","two","three"};
printf ("Value for TWO is %s\n",strNumbers[TWO]);

غير متفرق ، أعني غير شكل

typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;

منذ أن لديه ثغرات كبيرة في ذلك.

وميزة هذا الأسلوب هو أنه وضع تعاريف enums و سلاسل بالقرب من بعضها البعض ؛ بعد بيان التبديل في وظيفة spearates لهم.هذا يعني أنك أقل عرضة تغيير واحد دون الآخر.

محاولة تحويل C++ enums إلى سلاسل.على تعليقات وقد التحسينات التي حل مشكلة عند التعداد البنود التعسفية القيم.

استخدام دفعة::المعالج يجعل من الممكن حل أنيق كما يلي:

الخطوة 1:يتضمن الملف رأس:

#include "EnumUtilities.h"

الخطوة 2:تعلن تعداد وجوه مع بناء الجملة التالي:

MakeEnum( TestData,
         (x)
         (y)
         (z)
         );

الخطوة 3:استخدام البيانات الخاصة بك:

الحصول على عدد من العناصر:

td::cout << "Number of Elements: " << TestDataCount << std::endl;

الحصول المرتبطة السلسلة:

std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;

الحصول على قيمة تعداد من يرتبط سلسلة:

std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
std::cout << "Value of z is " << TestData2Enum("z") << std::endl;

هذه تبدو نظيفة وصغيرة الحجم ، مع أية ملفات إضافية تشمل.مدونة كتبت في EnumUtilities.ح هو ما يلي:

#include <boost/preprocessor/seq/for_each.hpp>
#include <string>

#define REALLY_MAKE_STRING(x) #x
#define MAKE_STRING(x) REALLY_MAKE_STRING(x)
#define MACRO1(r, data, elem) elem,
#define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
#define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;


#define MakeEnum(eName, SEQ) \
    enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
    last_##eName##_enum}; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    }; \
    static enum eName eName##2Enum(const std::string eStrEl) \
    { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
        return (enum eName)0; \
    };

هناك بعض الحد ، أيهم من دفعة::preprocessor.في هذه الحالة, قائمة الثوابت لا يمكن أن يكون أكبر من 64 العناصر.

تتبع نفس المنطق يمكن أن نفكر أيضا إلى إنشاء متفرق التعداد:

#define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
#define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
#define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
#define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));

#define MakeEnumEx(eName, SEQ) \
    enum eName { \
    BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
    last_##eName##_enum }; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    };  

في هذه الحالة بناء الجملة هو:

MakeEnumEx(TestEnum,
           ((x,))
           ((y,=1000))
           ((z,))
           );

استخدام مماثلة على النحو الوارد أعلاه (ناقص ايناميه##2Enum وظيفة ، التي قد تتمكن من محاولة استقراء من الجملة السابقة).

لقد اختبرت ذلك على mac و linux ، ولكن كن على علم بأن دفعة::المعالج قد لا يكون محمول.

من خلال دمج بعض التقنيات هنا جئت حتى مع أبسط شكل:

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

إذا كنت تستخدم دول مجلس التعاون الخليجي ، من الممكن استخدام:

const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};

ثم فقط اتصل على سبيل المثال

enum_to_string_map[enum1]

تحقق من الأفكار في مو ديناميات مختبرات البحوث - أرشيف المدونة.لقد وجدت هذا في وقت سابق من هذا العام - نسيت بالضبط السياق حيث جئت عبر هذا و قد تكيفت في هذه المدونة.يمكننا مناقشة مزايا إضافة هـ في الجبهة ؛ فإنه ينطبق على المشكلة المحددة الموجهة ، ولكن ليس جزء من الحل العام.خبأت هذا في بلدي 'المقالات القصيرة' مجلد - أحتفظ مثيرة للاهتمام قصاصات من التعليمات البرمجية في حالة أريد لهم في وقت لاحق.أنا أشعر بالحرج أن أقول أنني لم أحافظ على مذكرة من أين جاءت هذه الفكرة من في ذلك الوقت.

الرأس:paste1.ح

/*
@(#)File:           $RCSfile: paste1.h,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2008/05/17 21:38:05 $
@(#)Purpose:        Automated Token Pasting
*/

#ifndef JLSS_ID_PASTE_H
#define JLSS_ID_PASTE_H

/*
 * Common case when someone just includes this file.  In this case,
 * they just get the various E* tokens as good old enums.
 */
#if !defined(ETYPE)
#define ETYPE(val, desc) E##val,
#define ETYPE_ENUM
enum {
#endif /* ETYPE */

   ETYPE(PERM,  "Operation not permitted")
   ETYPE(NOENT, "No such file or directory")
   ETYPE(SRCH,  "No such process")
   ETYPE(INTR,  "Interrupted system call")
   ETYPE(IO,    "I/O error")
   ETYPE(NXIO,  "No such device or address")
   ETYPE(2BIG,  "Arg list too long")

/*
 * Close up the enum block in the common case of someone including
 * this file.
 */
#if defined(ETYPE_ENUM)
#undef ETYPE_ENUM
#undef ETYPE
ETYPE_MAX
};
#endif /* ETYPE_ENUM */

#endif /* JLSS_ID_PASTE_H */

مثال المصدر:

/*
@(#)File:           $RCSfile: paste1.c,v $
@(#)Version:        $Revision: 1.2 $
@(#)Last changed:   $Date: 2008/06/24 01:03:38 $
@(#)Purpose:        Automated Token Pasting
*/

#include "paste1.h"

static const char *sys_errlist_internal[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) desc,
#include "paste1.h"
    0
#undef ETYPE
};

static const char *xerror(int err)
{
    if (err >= ETYPE_MAX || err <= 0)
        return "Unknown error";
    return sys_errlist_internal[err];
}

static const char*errlist_mnemonics[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) [E ## val] = "E" #val,
#include "paste1.h"
#undef ETYPE
};

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 0; i < ETYPE_MAX; i++)
    {
        printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
    }
    return(0);
}

ليس بالضرورة في العالم أنظف استخدام C قبل المعالج - ولكن هذا لا يمنع كتابة المواد عدة مرات.

إذا كان التعداد المؤشر 0 القائم ، يمكنك وضع أسماء في مجموعة من شار* و مؤشر عليها مع التعداد القيمة.

#define stringify( name ) # name

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

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

مزيد من المناقشات بشأن هذه الطريقة

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

لقد خلق بسيط قالب فئة streamable_enum يستخدم تيار المشغلين << و >> ويقوم على std::map<Enum, std::string>:

#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP

#include <iostream>
#include <string>
#include <map>

template <typename E>
class streamable_enum
{
public:
    typedef typename std::map<E, std::string> tostr_map_t;
    typedef typename std::map<std::string, E> fromstr_map_t;

    streamable_enum()
    {}

    streamable_enum(E val) :
        Val_(val)
    {}

    operator E() {
        return Val_;
    }

    bool operator==(const streamable_enum<E>& e) {
        return this->Val_ == e.Val_;
    }

    bool operator==(const E& e) {
        return this->Val_ == e;
    }

    static const tostr_map_t& to_string_map() {
        static tostr_map_t to_str_(get_enum_strings<E>());
        return to_str_;
    }

    static const fromstr_map_t& from_string_map() {
        static fromstr_map_t from_str_(reverse_map(to_string_map()));
        return from_str_;
    }
private:
    E Val_;

    static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
        fromstr_map_t sToE;
        for (auto pr : eToS) {
            sToE.emplace(pr.second, pr.first);
        }
        return sToE;
    }
};

template <typename E>
streamable_enum<E> stream_enum(E e) {
    return streamable_enum<E>(e);
}

template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
    // \todo throw an appropriate exception or display compile error/warning
    return {};
}

template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
    auto& mp = streamable_enum<E>::to_string_map();
    auto res = mp.find(e);
    if (res != mp.end()) {
        os << res->second;
    } else {
        os.setstate(std::ios_base::failbit);
    }
    return os;
}

template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
    std::string str;
    is >> str;
    if (str.empty()) {
        is.setstate(std::ios_base::failbit);
    }
    auto& mp = streamable_enum<E>::from_string_map();
    auto res = mp.find(str);
    if (res != mp.end()) {
        e = res->second;
    } else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

#endif

الاستخدام:

#include "streamable_enum.hpp"

using std::cout;
using std::cin;
using std::endl;

enum Animal {
    CAT,
    DOG,
    TIGER,
    RABBIT
};

template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
    return {
        { CAT, "Cat"},
        { DOG, "Dog" },
        { TIGER, "Tiger" },
        { RABBIT, "Rabbit" }
    };
}

int main(int argc, char* argv []) {
    cout << "What animal do you want to buy? Our offering:" << endl;
    for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
    }
    streamable_enum<Animal> anim;
    cin >> anim;
    if (!cin) {
        cout << "We don't have such animal here." << endl;
    } else if (anim == Animal::TIGER) {
        cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
    } else {
        cout << "Here you are!" << endl;
    }

    return 0;
}

هنا هو الحل باستخدام وحدات الماكرو مع الميزات التالية:

  1. فقط كتابة كل قيمة التعداد مرة واحدة, حتى لا تكون هناك قوائم مزدوجة للحفاظ على

  2. لا تبقي التعداد القيم في ملف منفصل في وقت لاحق #وشملت كي أكتب أينما أريد

  3. لا محل التعداد نفسه, أنا لا تزال تريد أن يكون نوع التعداد محددة ، ولكن بالإضافة إلى ذلك أريد أن تكون قادرة على خريطة كل التعداد اسم السلسلة المقابلة (أن لا تؤثر على التعليمات البرمجية القديمة)

  4. البحث يجب أن تكون سريعة ، لذلك يفضل أن لا تبديل الحال ، بالنسبة لأولئك ضخمة enums

https://stackoverflow.com/a/20134475/1812866

اعتقدت أن حل مثل دفعة.فيوجن واحدة من أجل التكيف مع البنيات والطبقات سيكون لطيفا, لقد كان ذلك في بعض نقطة, استخدام enums والانصهار تسلسل.

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

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
    R, unused, ENUMERATION_ENTRY)                                               \
    case ENUMERATION_ENTRY:                                                     \
      return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
    break;                                                                      

/**
 * \brief Adapts ENUM to reflectable types.
 *
 * \param ENUM_TYPE To be adapted
 * \param ENUMERATION_SEQ Sequence of enum states
 */
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
    inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
      switch (enum_value) {                                                     \
      BOOST_PP_SEQ_FOR_EACH(                                                    \
          SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
          unused, ENUMERATION_SEQ)                                              \
        default:                                                                \
          return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
      }                                                                         \
    }                                                                           \
                                                                                \
    inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
      os << to_string(value);                                                   \
      return os;                                                                \
    }

#endif

القديمة الإجابة أدناه سيئة جدا, يرجى عدم استخدام هذا.:)

القديمة الجواب:

لقد تم البحث في الطريقة التي يحل هذه المشكلة دون تغيير كثيرا enums إعلان الجملة.جئت إلى الحل الذي يستخدم المعالج لاسترداد سلسلة من stringified التعداد الإعلان.

أنا قادرة على تحديد غير متفرق enums مثل هذا :

SMART_ENUM(State, 
    enum State {
        RUNNING,
        SLEEPING, 
        FAULT, 
        UNKNOWN
    })

وأنا يمكن أن تتفاعل معهم بطرق مختلفة:

// With a stringstream
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();

//Directly to stdout
std::cout << State::FAULT << std::endl;

//to a string
std::string myStr = State::to_string(State::FAULT);

//from a string
State::State myEnumVal = State::from_string(State::FAULT);

استنادا إلى التعاريف التالية :

#define SMART_ENUM(enumTypeArg, ...)                                                     \
namespace enumTypeArg {                                                                  \
    __VA_ARGS__;                                                                         \
    std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
            os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
            return os;                                                                   \
    }                                                                                    \
                                                                                     \
    std::string to_string(const enumTypeArg& val) {                                      \
            return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
    }                                                                                    \
                                                                                     \
    enumTypeArg from_string(const std::string &str) {                                    \
            return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
    }                                                                                    \
}                                                                                        \


namespace swissarmyknife { namespace enums {

    static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            if (enumVal == count) {
                std::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
                return identifiersSubset;
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("The enum declaration provided doesn't contains this state.");
    }                                                  

    template <typename EnumType>
    static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            std::string identifiersSubset = identifiers.substr(0, found);
            size_t beginId = identifiersSubset.find_last_of("{,");
            identifiersSubset = identifiersSubset.substr(beginId+1);
            boost::algorithm::trim(identifiersSubset);

            if (identifiersSubset == enumStr) {
                return static_cast<EnumType>(count);
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("No valid enum value for the provided string");
    }                      

}}

عندما كنت سوف تحتاج دعم متفرق enum و عندما يكون لدي المزيد من الوقت سوف تحسين to_string و from_string تطبيقات مع دفعة::xpressive ، ولكن هذا سوف التكاليف في تجميع الوقت لأنه من المهم النموذجيه إجراء للتنفيذ ولدت من المرجح أن يكون أكبر حقا.ولكن هذا له ميزة أنه سيكون أكثر قابلية للقراءة و maintanable من هذا القبيح دليل التلاعب سلسلة كود. :D

وإلا اعتدت دائما دفعة::bimap لأداء مثل هذه التعيينات بين enums قيمة سلسلة ، ولكن يجب أن يكون الحفاظ عليها يدويا.

لأنني أفضل عدم استخدام وحدات الماكرو لجميع الأسباب المعتادة ، أنا استخدم أكثر محدودية الكلي حل له ميزة حفظ إعلان التعداد الكلي مجانا.مساوئ تشمل الحاجة إلى نسخ ولصق الكلي defintion لكل التعداد ، وبعد صراحة إضافة ماكرو الاحتجاج عند إضافة القيم إلى التعداد.

std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
    switch (cs) {
    HANDLE(CaptureState::UNUSED)
    HANDLE(CaptureState::ACTIVE)
    HANDLE(CaptureState::CLOSED)
    }
    return os;
#undef HANDLE
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top