এনাম টাইপ ভেরিয়েবলকে স্ট্রিংয়ে কীভাবে রূপান্তর করবেন?


128

এনাম টাইপের যে ভেরিয়েবলের মান দেখানোর জন্য প্রিন্টফ কিভাবে করবেন? এই ক্ষেত্রে:

typedef enum {Linux, Apple, Windows} OS_type; 
OS_type myOS = Linux;

এবং আমার যা দরকার তা হ'ল কিছুটা

printenum(OS_type, "My OS is %s", myOS);

যা অবশ্যই একটি স্ট্রিং "লিনাক্স" দেখায়, কোনও পূর্ণসংখ্যা নয়।

আমি মনে করি, প্রথমে আমাকে স্ট্রিংগুলির মান-সূচকযুক্ত অ্যারে তৈরি করতে হবে। তবে আমি জানি না এটি করার সবচেয়ে সুন্দর উপায় কিনা। এটা কি আদৌ সম্ভব?


2
আপনার চয়ন করুন: এখানে , এখানে , এখানে । এটি একটি গোলমাল, তারা সব আধা নকল।
রুবেনভ

আর একটি , এটি আরও সম্পূর্ণ।
বিট 2 শিফট 0 '21

সম্ভাব্য সদৃশ stackoverflow.com/questions/207976/... সি ++ এবং জন্য stackoverflow.com/questions/9907160/... সি জন্য
সিরো Santilli郝海东冠状病六四事件法轮功

এত উত্তর এখনও কোনও "
এনাম

উত্তর:


70

এটি করার কোনও সুন্দর উপায় নেই। এনাম দ্বারা সূচিযুক্ত স্ট্রিংগুলির একটি অ্যারে সেট আপ করুন।

যদি আপনি প্রচুর আউটপুট করেন তবে আপনি কোনও অপারেটরকে সংজ্ঞায়িত করতে পারেন << যা এনাম প্যারামিটার নেয় এবং আপনার জন্য অনুসন্ধান করে।


2
এছাড়াও আপনি সংকলন সময়ে পরীক্ষা করতে পারেন যে আপনার অ্যারেতে স্ট্রিংগুলির প্রত্যাশিত সংখ্যা রয়েছে।
মার্খ 44

2
আমি জানি যে আমি এর সাথে বিশাল সংখ্যালঘুতে আছি, তবে আমার মতো প্রোগ্রামাররা যারা ভাষার তাত্পর্যপূর্ণ কমতিগুলি সমাধান করতে বৃহত তৃতীয় পক্ষের লাইব্রেরি এবং / অথবা মার্কো-রেড্ড কোডের উপর নির্ভর করতে চান না, আমি এটি বলে মনে করি আজকের স্ট্যান্ডার্ডের জন্য সহজতম এবং শুদ্ধতম সমাধান by +1
সিন্ডোগ

13
@ সিন্ডোগ তারপরে আপনার প্রোডাকশন কোডে দীর্ঘ 56 এনুমিরেটর এই প্রোগ্রামার দ্বারা অতিরিক্ত চাপের অতিরিক্ত ছাড়িয়ে অতিরিক্ত ছাড়ের বৈশিষ্ট্য প্রকাশ করার জন্য আপডেট করে এবং সেই এনাম-ইনডেক্সড অ্যারে আপডেট করতে ভুলে যায়। এটি লক্ষ করা যায় না, কারণ সম্পর্কিত মুদ্রণ সুবিধাটি কেবল অ্যাপ্লিকেশন ডিবাগিং কোড দ্বারা ব্যবহৃত হয়। ২ মাস পরে, আপনি প্রথমে সেই ডিবাগ কোডটি কার্যকর করেন: এটি তখন আপনাকে ভুল তথ্য দেয়, সুতরাং আপনি প্রথমে ডিবাগ কোডটি ডিবাগ করতে হয়েছিল তা বুঝতে পেরে আপনি এই ভুল তথ্যের উপর ভিত্তি করে অর্ধ দিনের বিল্ডিং অনুমানগুলি হারাবেন: নকশা সুস্পষ্ট সদৃশ উপর নির্ভর করে।
বিজ্ঞাপন এন

1
@ অ্যাডএন যে নকশাটি ভুল। এনাম থেকে মানব-পঠনযোগ্য স্ট্রিংয়ের ম্যাপিং এনাম মান অনুসারে সূচিযুক্ত স্ট্রিংয়ের অ্যারে হিসাবে প্রয়োগ করা উচিত নয়। আপনার অভিজ্ঞতা (সম্ভবত) এটি কেন তা দেখায়। ম্যাপিংটি (এনাম, স্ট্রিং) জোড়াগুলির এক্সপ্লিটি অ্যারে হওয়া উচিত, সুতরাং যদি আপনি আপনার নতুন এনাম মানের জন্য একটি এন্ট্রি যুক্ত করতে ভুলে যান তবে আপনি "???" পাবেন আউটপুট হিসাবে, তবে কমপক্ষে এটি আপনার সমস্ত অন্যান্য এনামের নাম স্ক্রু করবে না।
ব্রিবুব

8
@ অ্যাডএন আপনার দৃশ্যের কারণেই আমি অ্যারের পরিবর্তে কোনও সুইচ (কোনও ডিফল্ট ধারা ছাড়াই) যুক্ত একটি ফাংশন পছন্দ করি এবং বিল্ড ফাইলে সংকলক সুইচগুলি সেট করার জন্য একটি এনামের উপরে স্যুইচ করার জন্য একটি ত্রুটি জারি করে যা সমস্ত কভার করে না সম্ভাব্য মান। প্রাসঙ্গিক সুইচ বিবৃতি আপডেট না করে একটি নতুন এনাম এন্ট্রি যুক্ত করা সংকলনের ত্রুটির কারণ হতে পারে।
ডিভেজেক

131

নিষ্কলুষ সমাধানটি অবশ্যই প্রতিটি গণনার জন্য একটি ফাংশন লিখতে হয় যা স্ট্রিংয়ে রূপান্তর সম্পাদন করে:

enum OS_type { Linux, Apple, Windows };

inline const char* ToString(OS_type v)
{
    switch (v)
    {
        case Linux:   return "Linux";
        case Apple:   return "Apple";
        case Windows: return "Windows";
        default:      return "[Unknown OS_type]";
    }
}

এটি অবশ্য রক্ষণাবেক্ষণের বিপর্যয়। বুস্ট.প্রি প্রসেসর লাইব্রেরির সাহায্যে, যা সি এবং সি ++ কোড উভয়ের সাথেই ব্যবহার করা যেতে পারে, আপনি সহজেই প্রিপ্রোসেসরের সুবিধা নিতে পারেন এবং এটি আপনার জন্য এই ফাংশনটি তৈরি করতে দেয়। প্রজন্মের ম্যাক্রো নিম্নরূপ:

#include <boost/preprocessor.hpp>

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

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const char* ToString(name v)                                       \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,          \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }

প্রথম ম্যাক্রো (শুরু দিয়ে X_) দ্বিতীয় দ্বারা অভ্যন্তরীণভাবে ব্যবহৃত হয়। দ্বিতীয় ম্যাক্রো প্রথমে গণনা তৈরি করে, তার পরে একটি ToStringফাংশন তৈরি করে যা সেই ধরণের একটি অবজেক্ট নেয় এবং গণকের নামটিকে স্ট্রিং হিসাবে প্রত্যাবর্তন করে (এই বাস্তবায়নটি, প্রকৃত কারণে, প্রয়োজনে গণকের অনন্য মানগুলিতে মানচিত্রের প্রয়োজন হয়)।

সি ++ এ আপনি তার পরিবর্তে ওভারলোড ToStringহিসাবে ফাংশনটি প্রয়োগ করতে পারেন operator<<, তবে আমি মনে করি ToStringমানটিকে স্ট্রিং ফর্মে রূপান্তর করার জন্য একটি স্পষ্টত প্রয়োজন " " এটি কিছুটা পরিষ্কার think

ব্যবহারের উদাহরণ হিসাবে, আপনার OS_typeঅঙ্কটি নিম্নলিখিত হিসাবে সংজ্ঞায়িত হবে:

DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows))

ম্যাক্রো প্রথম দেখায় যেমন এটি অনেক কাজ, এবং OS_typeবিদেশী দেখায় তার সংজ্ঞা , মনে রাখবেন যে আপনাকে একবার ম্যাক্রো লিখতে হবে, তারপরে আপনি এটি প্রতিটি গণনার জন্য ব্যবহার করতে পারবেন। আপনি এতে অতিরিক্ত কার্যকারিতা যুক্ত করতে পারেন (উদাহরণস্বরূপ, এনক্রো রূপান্তরকরণের একটি স্ট্রিং-ফর্ম) খুব বেশি ঝামেলা ছাড়াই এবং এটি রক্ষণাবেক্ষণের সমস্যাটি পুরোপুরি সমাধান করে, যেহেতু আপনি যখন ম্যাক্রোকে ডাকেন তখন আপনাকে কেবল একবার নামগুলি সরবরাহ করতে হবে।

অঙ্কটি তখন ব্যবহার করা যেতে পারে যেমন এটি সাধারণভাবে সংজ্ঞায়িত হয়:

#include <iostream>

int main()
{
    OS_type t = Windows;
    std::cout << ToString(t) << " " << ToString(Apple) << std::endl;
}

এই পোস্টে কোড স্নিপেটগুলি, #include <boost/preprocessor.hpp>লাইন দিয়ে শুরু করে , সমাধানটি দেখানোর জন্য পোস্ট হিসাবে সংকলন করা যেতে পারে।

এই নির্দিষ্ট সমাধানটি সি ++ এর জন্য কারণ এটি সি ++ - নির্দিষ্ট সিনট্যাক্স (যেমন, না typedef enum) এবং ফাংশন ওভারলোডিং ব্যবহার করে তবে সি দিয়েও এই কাজটি করা সহজবোধ্য হবে।


7
+1, বাস্তবায়ন যন্ত্রপাতি ভীতিকর তবে শেষ ইন্টারফেসটি কমনীয়তার জন্য পরাজিত করা শক্ত।
deft_code

4
এটি আপনাকে এনাম পূর্ণসংখ্যার মানগুলি দেওয়ার অনুমতি দেবে কি সেভাবেই আছে। উদাহরণস্বরূপ, উইন্ডোজ 3, লিনাক্স 5 এবং অ্যাপল 7 হবে?
চিহ্নিত করুন

4
হ্যাঁ, আপনি এরপরে পরিবর্তন করতে পারেন (Windows)এবং উপযুক্ত লিখিতভাবে (Windows, 3)প্রতিস্থাপন করতে পারেন । আমার কাছে এই সহজ কাজের উদাহরণ নেই তবে আপনি চাইলে আমি একটি লিখতে পারি। BOOST_PP_SEQ_ENUMBOOST_PP_SEQ_FOR_EACH
জেমস ম্যাকনেলিস

2
@ জেমসম্যাকনেলিস আমি অবশ্যই একটি কোডের একটি উদাহরণ চাই যা মার্ক যা বলেছিল তা পূরণ করে, আপনি কি আমাদের পথ দেখানোর জন্য এত দয়াবান হবেন? :)
ওমর রাভিভ

2
দ্রষ্টব্য: বুস্ট প্রিপ্রোসেসরটির 256 টি উপাদানের একটি হার্ড সীমা রয়েছে। বৃহত্তর এনামগুলির জন্য, একটি আলাদা সমাধান প্রয়োজন।
shshin

32

এটি প্রাক প্রসেসর ব্লক

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define DECL_ENUM_ELEMENT( element ) #element
    #define BEGIN_ENUM( ENUM_NAME ) char* gs_##ENUM_NAME [] =
    #define END_ENUM( ENUM_NAME ) ; char* GetString##ENUM_NAME(enum \
            tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif

এনাম সংজ্ঞা

BEGIN_ENUM(Os_type)
{
    DECL_ENUM_ELEMENT(winblows),
    DECL_ENUM_ELEMENT(hackintosh),
} END_ENUM(Os_type)

কল করে কল করুন

GetStringOs_type(winblows);

এখান থেকে নেওয়া হয়েছে । কিভাবে শীতল হয় ? :)


1
এটি যখন কেবলমাত্র আপনার এনামের 256 এরও বেশি উপাদান থাকে তখনই এটি সমাধান হয়।
dshin

8

std::map<OS_type, std::string>কী হিসাবে এনাম, এবং মান হিসাবে স্ট্রিং প্রতিনিধিত্ব করে এটি ব্যবহার করুন এবং স্থাপন করুন , তারপরে আপনি এগুলি করতে পারেন:

printf("My OS is %s", enumMap[myOS].c_str());
std::cout << enumMap[myOS] ;

7

সি এনামগুলির সমস্যাটি হ'ল এটি কোনও নিজস্ব নয়, যেমন এটি সি ++ তে রয়েছে। সি-এ এনাম হ'ল অবিচ্ছেদ্য মানগুলিতে শনাক্তকারীদের মানচিত্রের একটি উপায়। হ্যাঁ ওটাই. এজন্য একটি এনাম মান পূর্ণসংখ্যার মানগুলির সাথে বিনিময়যোগ্য।

আপনি সঠিকভাবে অনুমান হিসাবে, একটি ভাল উপায় হ'ল এনাম মান এবং একটি স্ট্রিংয়ের মধ্যে একটি ম্যাপিং তৈরি করা। উদাহরণ স্বরূপ:

char * OS_type_label[] = {
    "Linux",
    "Apple",
    "Windows"
};

আমি ধরে নিয়েছি - আপাতদৃষ্টিতে ভুলভাবে - প্রোগ্রামিংয়ের ভাষা সি পর্যন্ত সীমাবদ্ধ
অ্যান্ড্রু

1
আপনি একটি বিট বন্ধ হয়, enum হয় সি সমাকলন পরিগণনা প্রকার ধ্রুবক ধরনের ধরনের intএবং এর enumটাইপ, যার মাধ্যমে তারা সংজ্ঞায়িত করা হয়, আপনি কি বলতে বোঝানো সম্ভবত নেই। তবে প্রশ্নটির সাথে এর কী আছে তা আমি মোটেও দেখতে পাচ্ছি না।
জেনস গুস্ট্ট

7

আমি জেমস ' , হাওয়ার্ডস এবং আডারের সমাধানগুলি একত্রিত করেছি এবং আরও জেনেরিক বাস্তবায়ন তৈরি করেছি:

  • প্রতিটি মান উপাদানগুলির জন্য ইন্ট মান এবং কাস্টম স্ট্রিং প্রতিনিধিত্ব optionচ্ছিকভাবে সংজ্ঞায়িত করা যেতে পারে
  • "এনাম ক্লাস" ব্যবহৃত হয়

পুরো কোডটি বেলো লেখা আছে (এনাম সংজ্ঞায়নের জন্য "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" ব্যবহার করুন) ( অনলাইন ডেমো )।

#include <boost/preprocessor.hpp>
#include <iostream>

// ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ implementation is taken from:
// http://lists.boost.org/boost-users/2012/09/76055.php
//
// This macro do the following:
// input:
//      (Element1, "Element 1 string repr", 2) (Element2) (Element3, "Element 3 string repr")
// output:
//      ((Element1, "Element 1 string repr", 2)) ((Element2)) ((Element3, "Element 3 string repr"))
#define HELPER1(...) ((__VA_ARGS__)) HELPER2
#define HELPER2(...) ((__VA_ARGS__)) HELPER1
#define HELPER1_END
#define HELPER2_END
#define ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(sequence) BOOST_PP_CAT(HELPER1 sequence,_END)


// CREATE_ENUM_ELEMENT_IMPL works in the following way:
//  if (elementTuple.GetSize() == 4) {
//      GENERATE: elementTuple.GetElement(0) = elementTuple.GetElement(2)),
//  } else {
//      GENERATE: elementTuple.GetElement(0),
//  }
// Example 1:
//      CREATE_ENUM_ELEMENT_IMPL((Element1, "Element 1 string repr", 2, _))
//  generates:
//      Element1 = 2,
//
// Example 2:
//      CREATE_ENUM_ELEMENT_IMPL((Element2, _))
//  generates:
//      Element1,
#define CREATE_ENUM_ELEMENT_IMPL(elementTuple)                                          \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 4),                       \
    BOOST_PP_TUPLE_ELEM(0, elementTuple) = BOOST_PP_TUPLE_ELEM(2, elementTuple),        \
    BOOST_PP_TUPLE_ELEM(0, elementTuple)                                                \
),

// we have to add a dummy element at the end of a tuple in order to make 
// BOOST_PP_TUPLE_ELEM macro work in case an initial tuple has only one element.
// if we have a tuple (Element1), BOOST_PP_TUPLE_ELEM(2, (Element1)) macro won't compile.
// It requires that a tuple with only one element looked like (Element1,).
// Unfortunately I couldn't find a way to make this transformation, so
// I just use BOOST_PP_TUPLE_PUSH_BACK macro to add a dummy element at the end
// of a tuple, in this case the initial tuple will look like (Element1, _) what
// makes it compatible with BOOST_PP_TUPLE_ELEM macro
#define CREATE_ENUM_ELEMENT(r, data, elementTuple)                                      \
    CREATE_ENUM_ELEMENT_IMPL(BOOST_PP_TUPLE_PUSH_BACK(elementTuple, _))

#define DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, element)                                        \
    case enumName::element : return BOOST_PP_STRINGIZE(element);
#define DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, element, stringRepresentation)  \
    case enumName::element : return stringRepresentation;

// GENERATE_CASE_FOR_SWITCH macro generates case for switch operator.
// Algorithm of working is the following
//  if (elementTuple.GetSize() == 1) {
//      DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, elementTuple.GetElement(0))
//  } else {
//      DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, elementTuple.GetElement(0), elementTuple.GetElement(1))
//  }
//
// Example 1:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element1, "Element 1 string repr", 2))
//  generates:
//      case EnumName::Element1 : return "Element 1 string repr";
//
// Example 2:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element2))
//  generates:
//      case EnumName::Element2 : return "Element2";
#define GENERATE_CASE_FOR_SWITCH(r, enumName, elementTuple)                                                                                                 \
    BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 1),                                                                                       \
        DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple)),                                                          \
        DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple), BOOST_PP_TUPLE_ELEM(1, elementTuple))     \
    )


// DEFINE_ENUM_CLASS_WITH_ToString_METHOD final macro witch do the job
#define DEFINE_ENUM_CLASS_WITH_ToString_METHOD(enumName, enumElements)          \
enum class enumName {                                                           \
    BOOST_PP_SEQ_FOR_EACH(                                                      \
        CREATE_ENUM_ELEMENT,                                                    \
        0,                                                                      \
        ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)                     \
    )                                                                           \
};                                                                              \
inline const char* ToString(const enumName element) {                           \
        switch (element) {                                                      \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                GENERATE_CASE_FOR_SWITCH,                                       \
                enumName,                                                       \
                ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)             \
            )                                                                   \
            default: return "[Unknown " BOOST_PP_STRINGIZE(enumName) "]";       \
        }                                                                       \
}

DEFINE_ENUM_CLASS_WITH_ToString_METHOD(Elements,
(Element1)
(Element2, "string representation for Element2 ")
(Element3, "Element3 string representation", 1000)
(Element4, "Element 4 string repr")
(Element5, "Element5", 1005)
(Element6, "Element6 ")
(Element7)
)
// Generates the following:
//      enum class Elements {
//          Element1, Element2, Element3 = 1000, Element4, Element5 = 1005, Element6,
//      };
//      inline const char* ToString(const Elements element) {
//          switch (element) {
//              case Elements::Element1: return "Element1";
//              case Elements::Element2: return "string representation for Element2 ";
//              case Elements::Element3: return "Element3 string representation";
//              case Elements::Element4: return "Element 4 string repr";
//              case Elements::Element5: return "Element5";
//              case Elements::Element6: return "Element6 ";
//              case Elements::Element7: return "Element7";
//              default: return "[Unknown " "Elements" "]";
//          }
//      }

int main() {
    std::cout << ToString(Elements::Element1) << std::endl;
    std::cout << ToString(Elements::Element2) << std::endl;
    std::cout << ToString(Elements::Element3) << std::endl;
    std::cout << ToString(Elements::Element4) << std::endl;
    std::cout << ToString(Elements::Element5) << std::endl;
    std::cout << ToString(Elements::Element6) << std::endl;
    std::cout << ToString(Elements::Element7) << std::endl;

    return 0;
}

এটি এখন অবধি সেরা উত্তর
আর্নআট

6

এই সহজ উদাহরণটি আমার পক্ষে কাজ করেছিল। আশাকরি এটা সাহায্য করবে.

#include <iostream>
#include <string>

#define ENUM_TO_STR(ENUM) std::string(#ENUM)

enum DIRECTION{NORTH, SOUTH, WEST, EAST};

int main()
{
  std::cout << "Hello, " << ENUM_TO_STR(NORTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(SOUTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(EAST) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(WEST) << "!\n";
}

13
আপনার কাছে DIRECTION a = NORTH থাকলে কাজ করবে না; এবং তারপরে ENUM_TO_STR (ক) লিখুন
mathreadler

5

আপনি কি এই চেষ্টা করেছেন:

#define stringify( name ) # name

enum enMyErrorValue
  {
  ERROR_INVALIDINPUT = 0,
  ERROR_NULLINPUT,
  ERROR_INPUTTOOMUCH,
  ERROR_IAMBUSY
  };

const char* enMyErrorValueNames[] = 
  {
  stringify( ERROR_INVALIDINPUT ),
  stringify( ERROR_NULLINPUT ),
  stringify( ERROR_INPUTTOOMUCH ),
  stringify( ERROR_IAMBUSY )
  };

void vPrintError( enMyErrorValue enError )
  {
  cout << enMyErrorValueNames[ enError ] << endl;
  }

int main()
  {
  vPrintError((enMyErrorValue)1);
  }

stringify()ম্যাক্রো একটি স্ট্রিং মধ্যে আপনার কোডে কোনো টেক্সট চালু করতে ব্যবহার করা যেতে পারে, কিন্তু প্রথম বন্ধনী মধ্যে শুধুমাত্র যথাযথ পাঠ। এখানে কোনও ভেরিয়েবল ডেরেফারিং বা ম্যাক্রোর বিকল্প বা অন্য কোনও ধরণের জিনিস করা হয়নি।

http://www.cplusplus.com/forum/general/2949/


এটি আসলে শীর্ষে থাকবে, যদিও এটির মধ্যে প্রথমটি যথেষ্ট যথেষ্ট হবে :)
ফোলাট

ঠিকঠাক কাজ করে, তবে সংকলন ত্রুটি এড়াতে আপনার উপরের শীর্ষে # আইফ্যান্ডিফ স্ট্রিংফাই যুক্ত করা উচিত। ডিজিএমজেড এর পরামর্শ অনুসারে আমি এনএম টাইপ স্টাড :: স্ট্রিং হিসাবেও পরিবর্তন করেছি
আষ্টারকাস্টার

5

এখানে প্রচুর ভাল উত্তর রয়েছে তবে আমি ভেবেছিলাম কিছু লোক হয়তো আমার কাজে লাগবে। আমি এটি পছন্দ করি কারণ আপনি ম্যাক্রো সংজ্ঞায়িত করতে যে ইন্টারফেসটি ব্যবহার করেন এটি যতটা সহজ। এটি খুব সহজ কারণ আপনার কোনও অতিরিক্ত লাইব্রেরি অন্তর্ভুক্ত করতে হবে না - এটি সমস্ত সি ++ সহ আসে এবং এটির এমনকি দেরী সংস্করণ প্রয়োজন হয় না। আমি অনলাইনে বিভিন্ন জায়গা থেকে টুকরো টানলাম যাতে আমি এর সবগুলির জন্য ক্রেডিট নিতে পারি না, তবে আমি মনে করি এটি একটি নতুন উত্তর ওয়ারেন্টের পক্ষে যথেষ্ট অনন্য।

প্রথমে একটি শিরোলেখ ফাইল তৈরি করুন ... এটিকে এনামম্যাক্রোস। এই জাতীয় কিছু বা এটি কল করুন এবং এটি এতে রাখুন:

// Search and remove whitespace from both ends of the string
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { rit++; }
    return std::string(it, rit.base());
}

static void SplitEnumArgs(const char* szArgs, std::string Array[], int nMax)
{
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        Array[nIdx] = TrimEnumString(strSub);
        nIdx++;
    }
};
// This will to define an enum that is wrapped in a namespace of the same name along with ToString(), FromString(), and COUNT
#define DECLARE_ENUM(ename, ...) \
    namespace ename { \
        enum ename { __VA_ARGS__, COUNT }; \
        static std::string _Strings[COUNT]; \
        static const char* ToString(ename e) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            return _Strings[e].c_str(); \
        } \
        static ename FromString(const std::string& strEnum) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            for (int i = 0; i < COUNT; i++) { if (_Strings[i] == strEnum) { return (ename)i; } } \
            return COUNT; \
        } \
    }

তারপরে, আপনার মূল প্রোগ্রামে আপনি এটি করতে পারেন ...

#include "EnumMacros.h"
DECLARE_ENUM(OsType, Windows, Linux, Apple)

void main() {
    OsType::OsType MyOs = OSType::Apple;
    printf("The value of '%s' is: %d of %d\n", OsType::ToString(MyOs), (int)OsType::FromString("Apple"), OsType::COUNT);
}

যেখানে আউটপুট হবে >> 'অ্যাপল' এর মান: 4 এর 2

উপভোগ করুন!


এই বিশেষ পদ্ধতির বিষয়ে আমি যে জিনিসটি পছন্দ করি তা হ'ল এটি এনামের সাধারণ কমা বিচ্ছিন্ন সিনট্যাক্সের সাথে কাজ করে (যতক্ষণ না এটি এনামের মধ্যে কোনও মান নির্ধারণের কার্যাদি অন্তর্ভুক্ত না করে)। আমার ক্ষেত্রে আমাকে প্রচুর সদস্যের সাথে একটি বিদ্যমান এনামের সাথে কাজ করতে হয়েছিল, সুতরাং এটি বুস্ট পদ্ধতির চেয়ে নামা অনেক সহজ ছিল।
কৌতূহলীয়া

4

আপনার এনাম ইতিমধ্যে সংজ্ঞায়িত করা হয়েছে তা ধরে নিলে, আপনি জোড়ার একটি অ্যারে তৈরি করতে পারেন:

std::pair<QTask::TASK, QString> pairs [] = {
std::pair<OS_type, string>(Linux, "Linux"),
std::pair<OS_type, string>(Windows, "Windows"),
std::pair<OS_type, string>(Apple, "Apple"),
};

এখন, আপনি একটি মানচিত্র তৈরি করতে পারেন:

std::map<OS_type, std::string> stdmap(pairs, pairs + sizeof(pairs) / sizeof(pairs[0]));

এখন, আপনি মানচিত্র ব্যবহার করতে পারেন। যদি আপনার এনামটি পরিবর্তন করা হয় তবে আপনাকে অ্যারে জোড়া থেকে জোড় যুক্ত / অপসারণ করতে হবে []। আমি মনে করি যে সি ++ তে এনাম থেকে স্ট্রিংটি পাওয়া সর্বাধিক মার্জিত উপায়।


2
এখানে কিউটিটির দরকার নেই এমন ন্যায্য মন্তব্য ছাড়াও আরেকটি বিষয় হ'ল যে কেউ bimapযদি নামগুলি বিশ্লেষণ করতে এবং সেগুলি এনামগুলিতে পরিণত করতে চায় (উদাহরণস্বরূপ, একটি এক্সএমএল ফাইল থেকে) বুস্টের ব্যবহার করতে পারে ।
দিমিত্রি নেস্টারুক

4
জেনেরিক সি ++ প্রশ্নের ক্ষেত্রে কিউটি টাইপ ব্যবহার করা উচিত নয়
ভেক্টর

3

সি 99P99_DECLARE_ENUM এর জন্য পি 99 রয়েছে যা আপনাকে সহজেই এর enumমতো ঘোষণা করতে দেয় :

P99_DECLARE_ENUM(color, red, green, blue);

এবং তারপরে color_getname(A)রঙের নামের সাথে একটি স্ট্রিং পেতে ব্যবহার করুন ।


2

এখানে আমার সি ++ কোডটি রয়েছে:

/* 
 * File:   main.cpp
 * Author: y2k1234
 *
 * Created on June 14, 2013, 9:50 AM
 */

#include <cstdlib>
#include <stdio.h>

using namespace std;


#define MESSAGE_LIST(OPERATOR)                          \
                                       OPERATOR(MSG_A), \
                                       OPERATOR(MSG_B), \
                                       OPERATOR(MSG_C)
#define GET_LIST_VALUE_OPERATOR(msg)   ERROR_##msg##_VALUE
#define GET_LIST_SRTING_OPERATOR(msg)  "ERROR_"#msg"_NAME"

enum ErrorMessagesEnum
{
   MESSAGE_LIST(GET_LIST_VALUE_OPERATOR)
};
static const char* ErrorMessagesName[] = 
{
   MESSAGE_LIST(GET_LIST_SRTING_OPERATOR)
};

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

    int totalMessages = sizeof(ErrorMessagesName)/4;

    for (int i = 0; i < totalMessages; i++)
    {
        if (i == ERROR_MSG_A_VALUE)
        {
                printf ("ERROR_MSG_A_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_B_VALUE)
        {
                printf ("ERROR_MSG_B_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_C_VALUE)
        {
                printf ("ERROR_MSG_C_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else
        {
                printf ("??? => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
    }   

    return 0;
}

Output:

ERROR_MSG_A_VALUE => [0]=[ERROR_MSG_A_NAME]

ERROR_MSG_B_VALUE => [1]=[ERROR_MSG_B_NAME]

ERROR_MSG_C_VALUE => [2]=[ERROR_MSG_C_NAME]

RUN SUCCESSFUL (total time: 126ms)

2

পার্টিতে কিছুটা দেরি হলেও এখানে আমার সি ++ 11 সমাধান রয়েছে:

namespace std {
    template<> struct hash<enum_one> {
        std::size_t operator()(const enum_one & e) const {
            return static_cast<std::size_t>(e);
        }
    };
    template<> struct hash<enum_two> { //repeat for each enum type
        std::size_t operator()(const enum_two & e) const {
            return static_cast<std::size_t>(e);
        }
    };
}

const std::string & enum_name(const enum_one & e) {
    static const std::unordered_map<enum_one, const std::string> names = {
    #define v_name(n) {enum_one::n, std::string(#n)}
        v_name(value1),
        v_name(value2),
        v_name(value3)
    #undef v_name
    };
    return names.at(e);
}

const std::string & enum_name(const enum_two & e) { //repeat for each enum type
    .................
}

1
error: ‘hash’ is not a class template->#include <functional>
রাগেরো তুরা

2

আমার নিজস্ব পছন্দটি হ'ল পুনরাবৃত্ত টাইপিং এবং ম্যাক্রোগুলি বোঝা শক্ত এবং সাধারণ সংকলক স্পেসে ম্যাক্রো সংজ্ঞা চালু করা এড়ানো উভয়ই হ্রাস করা।

সুতরাং, শিরোলেখ ফাইলটিতে:

enum Level{
        /**
        * zero reserved for internal use
        */
        verbose = 1,
        trace,
        debug,
        info,
        warn,
        fatal
    };

static Level readLevel(const char *);

এবং সিপিপি বাস্তবায়ন হ'ল:

 Logger::Level Logger::readLevel(const char *in) { 
 #  define MATCH(x) if (strcmp(in,#x) ==0) return x; 
    MATCH(verbose);
    MATCH(trace);
    MATCH(debug);
    MATCH(info);
    MATCH(warn);
    MATCH(fatal);
 # undef MATCH
    std::string s("No match for logging level ");
    s += in;
    throw new std::domain_error(s);
 }

যত তাড়াতাড়ি আমরা এটি সম্পন্ন করেছি ম্যাক্রোর # অ্যান্ডাফ নোট করুন।


2

আমার সমাধান, বুস্ট ব্যবহার না করে:

#ifndef EN2STR_HXX_
#define EN2STR_HXX_

#define MAKE_STRING_1(str     ) #str
#define MAKE_STRING_2(str, ...) #str, MAKE_STRING_1(__VA_ARGS__)
#define MAKE_STRING_3(str, ...) #str, MAKE_STRING_2(__VA_ARGS__)
#define MAKE_STRING_4(str, ...) #str, MAKE_STRING_3(__VA_ARGS__)
#define MAKE_STRING_5(str, ...) #str, MAKE_STRING_4(__VA_ARGS__)
#define MAKE_STRING_6(str, ...) #str, MAKE_STRING_5(__VA_ARGS__)
#define MAKE_STRING_7(str, ...) #str, MAKE_STRING_6(__VA_ARGS__)
#define MAKE_STRING_8(str, ...) #str, MAKE_STRING_7(__VA_ARGS__)

#define PRIMITIVE_CAT(a, b) a##b
#define MAKE_STRING(N, ...) PRIMITIVE_CAT(MAKE_STRING_, N)     (__VA_ARGS__)


#define PP_RSEQ_N() 8,7,6,5,4,3,2,1,0
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG( ...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())

#define MAKE_ENUM(NAME, ...) enum NAME { __VA_ARGS__ };            \
  struct NAME##_str {                                              \
    static const char * get(const NAME et) {                       \
      static const char* NAME##Str[] = {                           \
                MAKE_STRING(PP_NARG(__VA_ARGS__), __VA_ARGS__) };  \
      return NAME##Str[et];                                        \
      }                                                            \
    };

#endif /* EN2STR_HXX_ */

এটি এখানে কীভাবে ব্যবহার করবেন তা এখানে

int main()
  {
  MAKE_ENUM(pippo, pp1, pp2, pp3,a,s,d);
  pippo c = d;
  cout << pippo_str::get(c) << "\n";
  return 0;
  }

2

প্রিপ্রসেসর ব্যবহার করে পার্টিতে আর একটি দেরী:

 1  #define MY_ENUM_LIST \
 2      DEFINE_ENUM_ELEMENT(First) \
 3      DEFINE_ENUM_ELEMENT(Second) \
 4      DEFINE_ENUM_ELEMENT(Third) \
 5  
 6  //--------------------------------------
 7  #define DEFINE_ENUM_ELEMENT(name) , name
 8  enum MyEnum {
 9      Zeroth = 0
10      MY_ENUM_LIST
11  };
12  #undef DEFINE_ENUM_ELEMENT
13 
14  #define DEFINE_ENUM_ELEMENT(name) , #name
15  const char* MyEnumToString[] = {
16      "Zeroth"
17      MY_ENUM_LIST
18  };
19  #undef DEFINE_ENUM_ELEMENT
20
21  #define DEFINE_ENUM_ELEMENT(name) else if (strcmp(s, #name)==0) return name;
22  enum MyEnum StringToMyEnum(const char* s){
23      if (strcmp(s, "Zeroth")==0) return Zeroth;
24      MY_ENUM_LIST
25      return NULL;
26  }
27  #undef DEFINE_ENUM_ELEMENT

(আমি কেবল লাইন নম্বরগুলিতে রেখেছি যাতে এটি সম্পর্কে কথা বলা সহজ um) এনামের উপাদানগুলি সংজ্ঞায়িত করতে আপনি 1-10 রেখাগুলি সম্পাদনা করেন। (আমি এটিকে "তালিকার ম্যাক্রো" বলেছি, কারণ এটি এমন একটি ম্যাক্রো যা জিনিসের একটি তালিকা তৈরি করে।

লাইন 7 অভ্যন্তরীণ ম্যাক্রোকে সংজ্ঞায়িত করে যাতে 8-10 লাইনে প্রকৃত এনাম ঘোষণাটি পূরণ করতে পারে। লাইন 12 অভ্যন্তরীণ ম্যাক্রোটিকে অপরিবর্তিত করেছে (কেবল সংকলক সতর্কবার্তাটি নিঃশব্দ করার জন্য)।

লাইন 14 অন্তর্নির্মিত ম্যাক্রোটিকে এমনভাবে সংজ্ঞায়িত করে যাতে এনাম উপাদানটির নামের একটি স্ট্রিং সংস্করণ তৈরি করা যায়। তারপরে লাইন 15-18 একটি অ্যারে তৈরি করে যা এনামের মানটিকে সংশ্লিষ্ট স্ট্রিংয়ে রূপান্তর করতে পারে।

লাইন 21-27 একটি ফাংশন জেনারেট করে যা স্ট্রিংটিকে এনাম মানের সাথে রূপান্তর করে, বা স্ট্রিংটির সাথে কোনওরূপ না মেলে NULL প্রদান করে।

এটি 0 তম উপাদানটি যেভাবে পরিচালনা করে তা এটি একটি খানিকটা জটিল। আমি আসলে অতীতে কাজ করেছি।

আমি স্বীকার করি যে এই প্রযুক্তিটি এমন লোকদের বিরক্ত করে যা প্রিপ্রোসেসরটি নিজেই আপনার জন্য কোড লেখার জন্য প্রোগ্রাম করা যেতে পারে think আমি মনে করি এটি পাঠযোগ্যতা এবং রক্ষণাবেক্ষণের মধ্যে পার্থক্য দৃ strongly ়ভাবে চিত্রিত করে । কোডটি পড়তে অসুবিধা হয় তবে এনামের কয়েকশ উপাদান থাকলে আপনি উপাদান যুক্ত করতে, অপসারণ করতে বা পুনর্বিন্যাস করতে পারেন এবং এখনও নিশ্চিত হন যে উত্পন্ন কোডটির কোনও ত্রুটি নেই।


"এক্স ম্যাক্রোস" খুব কমই যে কোনও সমস্যার একটি মার্জিত সমাধান। এই ক্ষেত্রে ম্যাক্রো আইটেমগুলি #define TEST_1 hello #define TEST_2 worldততক্ষণ কেবল সংজ্ঞায়িত করা typedef enum { TEST_1, TEST_2 } test_t;এবং তারপরে স্ট্রিংফাই ম্যাক্রো ব্যবহার করে একটি স্ট্রিং লুক-আপ টেবিল তৈরি করা আরও অনেক বেশি পঠনযোগ্য হবে : const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), }; ইতিমধ্যে অনুরূপ সমাধানগুলিতে ইঙ্গিত দেওয়া একাধিক উত্তর রয়েছে। আরও বেশি পঠনযোগ্য।
লন্ডিন

@ লন্ডিন: আমি কেবল দাবি করি 1) এটি সর্বাধিক আদিম সি সংকলক সহ কাজ করে এবং 2) উপাদান যুক্ত করা বা মোছা 1 লাইন সম্পাদনা।
মাইক ডুনলাভে

আমি আমার নিজের একটি উত্তর পোস্ট করেছি: stackoverflow.com/a/39877228/584518 । আশা করছি এটি এক্স ম্যাক্রো সমাধান থেকে কিছু দরিদ্র আত্মাকে বাঁচাবে will
লুন্ডিন

1
আমি আপনার সমাধান ব্যবহার। আমি মনে করি এটি সেরা। সি-সিনট্যাক্সটি এখনও রয়েছে যাতে আপনি বুঝতে পারেন কী হয় এবং তালিকাটি কেবল একবার সংজ্ঞায়িত করা হয়। আপনার DEFINE_ENUM_ELEMENT এ প্রবেশের পরে কমা রেখে আপনি 0 তম উপাদানটি সরিয়ে ফেলতে পারেন।

1

এখানে স্রেফ সি প্রসেসর ব্যবহার করে ওল্ড স্কুল পদ্ধতি (জিসিসিতে ব্যাপকভাবে ব্যবহৃত হত)'s আপনি যদি আলাদা ডেটা স্ট্রাকচার তৈরি করেন তবে দরকারী তবে তাদের মধ্যে ক্রমটি সামঞ্জস্য রাখতে হবে। Mylist.tbl এ এন্ট্রি অবশ্যই আরও জটিল কিছুতে প্রসারিত হতে পারে।

test.cpp:

enum {
#undef XX
#define XX(name, ignore) name ,
#include "mylist.tbl"
  LAST_ENUM
};

char * enum_names [] = {
#undef XX
#define XX(name, ignore) #name ,
#include "mylist.tbl"
   "LAST_ENUM"
};

এবং তারপরে mylist.tbl:

/*    A = enum                  */
/*    B = some associated value */
/*     A        B   */
  XX( enum_1 , 100)
  XX( enum_2 , 100 )
  XX( enum_3 , 200 )
  XX( enum_4 , 900 )
  XX( enum_5 , 500 )

1
এই কৌশলটি এক্স ম্যাক্রোস বলা হয়!
ওয়াটসিমোটো

0

সি ++ তে এটির মতো:

enum OS_type{Linux, Apple, Windows};

std::string ToString( const OS_type v )
{
  const std::map< OS_type, std::string > lut =
    boost::assign::map_list_of( Linux, "Linux" )(Apple, "Apple )( Windows,"Windows");
  std::map< OS_type, std::string >::const_iterator it = lut.find( v );
  if ( lut.end() != it )
    return it->second;
  return "NOT FOUND";
}

0
#include <EnumString.h>

http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C থেকে এবং তার পরে

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

0

আপনার পরামর্শের জন্য ধন্যবাদ জেমস। এটি খুব দরকারী ছিল তাই আমি অন্যভাবে কিছুটা অবদান রাখতে অন্যভাবে কার্যকর করেছি implemented

#include <iostream>
#include <boost/preprocessor.hpp>

using namespace std;

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

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF(r, data, elem) \
    if (BOOST_PP_SEQ_TAIL(data) ==                                     \
            BOOST_PP_STRINGIZE(elem)) return                           \
            static_cast<int>(BOOST_PP_SEQ_HEAD(data)::elem); else

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)         \
    enum class name {                                                  \
        BOOST_PP_SEQ_ENUM(enumerators)                                 \
    };                                                                 \
                                                                       \
    inline const char* ToString(name v)                                \
    {                                                                  \
        switch (v)                                                     \
        {                                                              \
            BOOST_PP_SEQ_FOR_EACH(                                     \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,   \
                name,                                                  \
                enumerators                                            \
            )                                                          \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";  \
        }                                                              \
    }                                                                  \
                                                                       \
    inline int ToEnum(std::string s)                                   \
    {                                                                  \
        BOOST_PP_SEQ_FOR_EACH(                                         \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF,       \
                (name)(s),                                             \
                enumerators                                            \
            )                                                          \
        return -1;                                                     \
    }


DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows));

int main(void)
{
    OS_type t = OS_type::Windows;

    cout << ToString(t) << " " << ToString(OS_type::Apple) << " " << ToString(OS_type::Linux) << endl;

    cout << ToEnum("Windows") << " " << ToEnum("Apple") << " " << ToEnum("Linux") << endl;

    return 0;
}

0

জেমস এর উত্তর প্রসারিত করতে, কেউ এনটাম মান সহ এনামকে সংজ্ঞায়িত করার জন্য কিছু উদাহরণ কোড চায়, আমারও এই প্রয়োজনীয়তা রয়েছে, তাই এখানে আমার উপায়:

প্রথমটি হ'ল অভ্যন্তরীণ ব্যবহারের ম্যাক্রো, যা FOR_Each দ্বারা ব্যবহৃত হয়:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE(r, data, elem)         \
    BOOST_PP_IF(                                                                \
        BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elem), 2),                           \
        BOOST_PP_TUPLE_ELEM(0, elem) = BOOST_PP_TUPLE_ELEM(1, elem),            \
        BOOST_PP_TUPLE_ELEM(0, elem) ),

এবং, এখানে ম্যাক্রো সংজ্ঞায়িত করা হয়েছে:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                  \
    enum name {                                                                 \
        BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE, \
                              0, enumerators) };

সুতরাং এটি ব্যবহার করার সময়, আপনি এটি লিখতে পছন্দ করতে পারেন:

DEFINE_ENUM_WITH_STRING_CONVERSIONS(MyEnum,
    ((FIRST, 1))
    ((SECOND))
    ((MAX, SECOND)) )

যা প্রসারিত হবে:

enum MyEnum
{
    FIRST = 1,
    SECOND,
    MAX = SECOND,
};

মূল ধারণাটি একটি এসইকিউকে সংজ্ঞায়িত করা হয় যা প্রতিটি উপাদানই একটি টিপল, তাই আমরা এনাম সদস্যের জন্য অতিরিক্ত মান রাখতে পারি put ফর_এইচ লুপে আইটেমটি TUPLE আকারটি পরীক্ষা করুন, যদি আকারটি 2 হয় তবে কোডটি KEY = VALUE এ প্রসারিত করুন, অন্যথায় কেবল টিপলির প্রথম উপাদানটি রাখুন।

কারণ ইনপুট এসইকিউ আসলে টিপলস, তাই আপনি যদি STRINGIZE ফাংশনগুলি সংজ্ঞায়িত করতে চান তবে আপনাকে প্রথমে ইনপুট এনুমিরেটরগুলির প্রাক-প্রক্রিয়া করতে হতে পারে, কাজটি করার জন্য এখানে ম্যাক্রো রয়েছে:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM(r, data, elem)           \
    BOOST_PP_TUPLE_ELEM(0, elem),

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ(enumerators)         \
    BOOST_PP_SEQ_SUBSEQ(                                                        \
        BOOST_PP_TUPLE_TO_SEQ(                                                  \
            (BOOST_PP_SEQ_FOR_EACH(                                             \
                DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM, 0, enumerators) \
            )),                                                                 \
            0,                                                                  \
            BOOST_PP_SEQ_SIZE(enumerators))

ম্যাক্রো কেবল DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQপ্রতিটি টিপলিতে প্রথম উপাদান রাখবে এবং পরে এসইকিউতে রূপান্তর করবে, এখন জেমসের কোডটি সংশোধন করুন, আপনার সম্পূর্ণ ক্ষমতা থাকবে।

আমার বাস্তবায়ন সম্ভবত সবচেয়ে সহজ নয়, সুতরাং যদি আপনি কোনও পরিষ্কার কোড না পান তবে আপনার রেফারেন্সের জন্য আমার।


0

খাঁটি স্ট্যান্ডার্ড সি এর পরিষ্কার, নিরাপদ সমাধান:

#include <stdio.h>

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

/* list of enum constants */
#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N
} test_t;

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

int main()
{  
  _Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
                 "Incorrect number of items in enum or look-up table");

  printf("%d %s\n", hello, test_str[hello]);
  printf("%d %s\n", world, test_str[world]);
  test_t x = world;
  printf("%d %s\n", x, test_str[x]);

  return 0;
}

আউটপুট

0 hello
1 world
1 world

যুক্তিসহ ব্যাখ্যা

মূল সমস্যাটি সমাধান করার সময় "সংশ্লিষ্ট স্ট্রিংগুলির সাথে এনাম ধ্রুবক রয়েছে", একটি বুদ্ধিমান প্রোগ্রামার নিম্নলিখিত প্রয়োজনীয়তা নিয়ে আসবে:

  • কোড পুনরাবৃত্তি ("DRY" নীতি) এড়ান principle
  • এনামের ভিতরে আইটেমগুলি যুক্ত করা বা অপসারণ করা হলেও কোডটি অবশ্যই স্কেবলযোগ্য, রক্ষণাবেক্ষণযোগ্য এবং নিরাপদ হতে হবে।
  • সমস্ত কোড উচ্চমানের হওয়া উচিত: পড়তে সহজ, বজায় রাখা সহজ।

প্রথম আবশ্যকতা এবং সম্ভবত দ্বিতীয়টিও বিভিন্ন কুটিল ম্যাক্রো সমাধান যেমন কুখ্যাত "এক্স ম্যাক্রো" কৌশল, বা ম্যাক্রো যাদুর অন্যান্য রূপগুলির সাথে পূরণ করা যেতে পারে। এই জাতীয় সমাধানগুলির সাথে সমস্যাটি হ'ল তারা আপনাকে রহস্যময় ম্যাক্রোগুলির সম্পূর্ণ অপঠনযোগ্য জগাখিচুড়ি দিয়ে চলে যায় - তারা উপরের তৃতীয় প্রয়োজনীয়তা পূরণ করে না।

এখানে কেবলমাত্র প্রয়োজনীয় জিনিসটির স্ট্রিং লুক-আপ টেবিল থাকা দরকার, যা আমরা এনাম ভেরিয়েবলকে সূচক হিসাবে ব্যবহার করে অ্যাক্সেস করতে পারি। এই জাতীয় একটি টেবিল অবশ্যই প্রাকৃতিকভাবে enum এবং তদ্বিপরীত সাথে সামঞ্জস্য করতে হবে। যখন তাদের একটি আপডেট করা হয়, অন্যটিকেও আপডেট করতে হবে, বা এটি কাজ করবে না।


কোড ব্যাখ্যা

মনে করুন আমাদের মতো এনাম আছে

typedef enum
{
  hello,
  world
} test_t;

এটিতে পরিবর্তন করা যেতে পারে

#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
} test_t;

এই ম্যাক্রো কনস্ট্যান্টগুলি এখন অন্য কোথাও ব্যবহার করা যেতে পারে এমন সুবিধা দিয়ে উদাহরণস্বরূপ একটি স্ট্রিং লুক-আপ টেবিল তৈরি করুন। প্রি-প্রসেসরকে ধ্রুবককে স্ট্রিনে রূপান্তর করা "স্ট্রিংফাই" ম্যাক্রো দিয়ে করা যেতে পারে:

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

এবং এটাই. ব্যবহার করে hello, আমরা মান 0 দিয়ে এনাম ধ্রুবক test_str[hello]পেতে ব্যবহার করে আমরা "হ্যালো" স্ট্রিংটি পাই।

এনাম এবং চেহারা আপ টেবিলটি সরাসরি সম্পর্কিত করতে, আমাদের তা নিশ্চিত করতে হবে যে সেগুলিতে খুব বেশি পরিমাণে আইটেম রয়েছে। যদি কেউ কোডটি বজায় রাখে এবং কেবল এনাম পরিবর্তন করে এবং চেহারা-সারণী বা তদ্বিপরীত না হয় তবে এই পদ্ধতিটি কার্যকর হবে না।

সমাধানটি হ'ল এনামটি আপনাকে জানাতে পারে যে এটিতে কতগুলি আইটেম রয়েছে। এর জন্য একটি সাধারণভাবে ব্যবহৃত সি ট্রিক রয়েছে, কেবল শেষে একটি আইটেম যুক্ত করুন, যা কেবল এনামের কতগুলি আইটেম রয়েছে তা বলার উদ্দেশ্য পূরণ করে:

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N  // will have value 2, there are 2 enum constants in this enum
} test_t;

সংকলনের সময় আমরা পরীক্ষা করতে পারি যে এনামে আইটেমের সংখ্যা লুক-টেবিলের আইটেমের সংখ্যার চেয়ে অনেক বেশি, বিশেষত সি 11 স্ট্যাটিক জোর দিয়ে:

_Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
               "Incorrect number of items in enum or look-up table");

(সি স্ট্যান্ডার্ডের পুরানো সংস্করণগুলিতেও স্ট্যাটিক অ্যাসেটস তৈরির জন্য কুৎসিত তবে সম্পূর্ণ কার্যকরী উপায় রয়েছে, যদি কেউ ডাইনোসর সংকলক ব্যবহারের প্রতি জোর দেয় C সি ++ এর ক্ষেত্রে এটি স্ট্যাটিক সংস্থানগুলিও সমর্থন করে))


পার্শ্ব নোট হিসাবে, সি 11 এ আমরা স্ট্রিংফাই ম্যাক্রো পরিবর্তন করে উচ্চতর ধরণের সুরক্ষাও অর্জন করতে পারি:

#define STRINGIFY(x) _Generic((x), int : STRF(x))

( intকারণ গণনার ধ্রুবকগুলি আসলে টাইপের int, না test_t)

এটি STRINGIFY(random_stuff)কোডকে সংকলন করা থেকে বিরত করবে ।


আপনি কী বলছেন তা আমি বুঝতে পেরেছি, তবে কথাটি রয়ে গেছে। আদর্শ পূর্ববর্তী পরিবর্তনগুলির জন্য ন্যূনতম সম্পাদনা প্রয়োজন (যেমন 1 লাইন)। (আমি মনে করি এটি ডিআরওয়াইর পিছনে কারণ)। সুতরাং এখানে যদি এনামের আকার 500 এর মতো হয় এবং আপনি মাঝখানে একটি নতুন উপাদান সন্নিবেশ করতে চান (বা সরিয়ে / পুনঃনামকরণ / সোয়াপ), কোডের কত লাইন আপনার অবশ্যই আবশ্যক পরিবর্তন করুন এবং আপনি ভুল করেন নি তা নিশ্চিত হতে আপনার কতটা চেকিং করতে হবে? কোডের অন্যান্য টুকরাও থাকতে পারে যা তালিকার প্রতিটি উপাদানটির জন্য অভিন্ন কিছু করে।
মাইক ডুনলাভে

এগুলিকে এক্স-ম্যাক্রোস বলে দেওয়ার জন্য ধন্যবাদ । আমি এটা জানতাম না। আমি যা দেখছি না তা হ'ল লোকেরা সাধারণভাবে তাদের অবজ্ঞা করছে।
মাইক ডুনলাভে

@ মাইকডুনলাভে এনামের আকারের বিষয়টি বিবেচনা না করেই আপনাকে ঠিক তিনটি লাইন পরিবর্তন করতে হবে: একটি #defineযোগ করুন, এনামের ঘোষণাপত্রে বর্ণিত সংজ্ঞাটি এবং বর্ণন সারণীতে একটি উল্লেখ যুক্ত করুন। আপনি যদি এই লাইনগুলি যুক্ত করার সময় বোকা হন, প্রোগ্রামটি সংকলন করবে না। আমি শনাক্তকারীগুলিতে যে সংখ্যাগুলি যুক্ত করেছি তা কোনওভাবেই বাধ্যতামূলক নয়, আপনি পাশাপাশি লিখতে পারেন #define APPLES helloএবং এর #define ORANGES worldপরেও চলতে typedef enum { APPES, ORANGES, TEST_N } test_t;পারেন।
লন্ডিন

@ মাইকডুনলাভে এক্স ম্যাক্রো সম্পর্কিত, তাদের বিরুদ্ধে যুক্তিগুলি কোনও ফাংশনের মতো ম্যাক্রোর বিরুদ্ধে আর্গুমেন্টের সমান। ফাংশন-জাতীয় ম্যাক্রোগুলির বিরুদ্ধে প্রচুর বৈধ সমালোচনা খুঁজে পেতে আপনাকে আর তাকাতে হবে না।
লন্ডিন

0

আমি যা তৈরি করেছি তা এই সাইটের এবং এখানে একই ধরণের প্রশ্নগুলির সাথে একত্রীকরণ। আমি এটি ভিজ্যুয়াল স্টুডিও 2013 তৈরি করেছি I আমি অন্য সংকলকগুলির সাথে এটি পরীক্ষা করিনি।

সবার আগে আমি ম্যাক্রোগুলির একটি সেট সংজ্ঞায়িত করি যা কৌশলগুলি করবে।

// concatenation macros
#define CONCAT_(A, B) A ## B
#define CONCAT(A, B)  CONCAT_(A, B)

// generic expansion and stringification macros
#define EXPAND(X)           X
#define STRINGIFY(ARG)      #ARG
#define EXPANDSTRING(ARG)   STRINGIFY(ARG)        

// number of arguments macros
#define NUM_ARGS_(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...) N
#define NUM_ARGS(...) EXPAND(NUM_ARGS_(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))

// argument extraction macros
#define FIRST_ARG(ARG, ...) ARG
#define REST_ARGS(ARG, ...) __VA_ARGS__

// arguments to strings macros
#define ARGS_STR__(N, ...)  ARGS_STR_##N(__VA_ARGS__)
#define ARGS_STR_(N, ...)   ARGS_STR__(N, __VA_ARGS__)
#define ARGS_STR(...)       ARGS_STR_(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

#define ARGS_STR_1(ARG)     EXPANDSTRING(ARG)
#define ARGS_STR_2(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_1(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_3(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_2(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_4(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_3(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_5(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_4(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_6(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_5(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_7(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_6(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_8(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_7(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_9(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_8(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_10(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_9(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_11(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_10(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_12(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_11(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_13(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_12(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_14(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_13(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_15(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_14(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_16(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_15(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_17(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_16(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_18(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_17(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_19(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_18(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_20(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_19(EXPAND(REST_ARGS(__VA_ARGS__)))
// expand until _100 or as much as you need

এর পরে একটি একক ম্যাক্রো সংজ্ঞায়িত করুন যা এনাম ক্লাস এবং স্ট্রিংগুলি পেতে ফাংশন তৈরি করবে।

#define ENUM(NAME, ...)                                                                                             \
    enum class NAME                                                                                                 \
    {                                                                                                               \
        __VA_ARGS__                                                                                                 \
    };                                                                                                              \
                                                                                                                    \
    static const std::array<std::string, NUM_ARGS(__VA_ARGS__)> CONCAT(NAME, Strings) = { ARGS_STR(__VA_ARGS__) };  \
                                                                                                                    \
    inline const std::string& ToString(NAME value)                                                                  \
    {                                                                                                               \
        return CONCAT(NAME, Strings)[static_cast<std::underlying_type<NAME>::type>(value)];                         \
    }                                                                                                               \
                                                                                                                    \
    inline std::ostream& operator<<(std::ostream& os, NAME value)                                                   \
    {                                                                                                               \
        os << ToString(value);                                                                                      \
        return os;                                                                                                  \
    }

এখন একটি এনাম টাইপ সংজ্ঞায়িত করা এবং এর জন্য স্ট্রিং রাখা সত্যিই সহজ হয়ে যায়। আপনাকে যা করতে হবে তা হ'ল:

ENUM(MyEnumType, A, B, C);

নিম্নলিখিত পরীক্ষাগুলি এটি পরীক্ষা করতে ব্যবহার করা যেতে পারে।

int main()
{
    std::cout << MyEnumTypeStrings.size() << std::endl;

    std::cout << ToString(MyEnumType::A) << std::endl;
    std::cout << ToString(MyEnumType::B) << std::endl;
    std::cout << ToString(MyEnumType::C) << std::endl;

    std::cout << MyEnumType::A << std::endl;
    std::cout << MyEnumType::B << std::endl;
    std::cout << MyEnumType::C << std::endl;

    auto myVar = MyEnumType::A;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::B;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::C;
    std::cout << myVar << std::endl;

    return 0;
}

এটি আউটপুট দেবে:

3
A
B
C
A
B
C
A
B
C

আমি বিশ্বাস করি এটি খুব পরিষ্কার এবং সহজেই ব্যবহারযোগ্য। কিছু সীমাবদ্ধতা রয়েছে:

  • আপনি এনাম সদস্যদের মান নির্ধারণ করতে পারবেন না।
  • এনাম সদস্যের মানগুলি সূচক হিসাবে ব্যবহৃত হয় তবে এটি ঠিক হওয়া উচিত, কারণ সবকিছুই একটি ম্যাক্রোতে সংজ্ঞায়িত হয়।
  • আপনি এটি কোনও শ্রেণীর ভিতরে এনাম টাইপ নির্ধারণ করতে ব্যবহার করতে পারবেন না।

আপনি যদি এই চারপাশে কাজ করতে পারেন। আমি মনে করি, বিশেষত এটি কীভাবে ব্যবহার করা যায়, এটি দুর্দান্ত এবং হেলান। সুবিধাদি:

  • ব্যবহার করা সহজ.
  • রানটাইমের সময় কোনও স্ট্রিং বিভক্ত হওয়া প্রয়োজন।
  • সংকলনের সময় পৃথক স্ট্রিং উপলব্ধ।
  • পড়তে সহজ. ম্যাক্রোগুলির প্রথম সেটটিতে অতিরিক্ত দ্বিতীয় প্রয়োজন হতে পারে তবে এটি আসলে জটিল নয়।

0

এই সমস্যার পরিষ্কার সমাধান হ'ল:

#define RETURN_STR(val, e) {if (val == e) {return #e;}}

std::string conv_dxgi_format_to_string(int value) {
    RETURN_STR(value, DXGI_FORMAT_UNKNOWN);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_FLOAT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_UINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_SINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_FLOAT);

    /* ... */

    return "<UNKNOWN>";
}

এই সমাধান সম্পর্কে ভাল জিনিস এটি সহজ এবং ফাংশন নির্মাণ অনুলিপি এবং প্রতিস্থাপনের মাধ্যমে সহজেই করা যেতে পারে। মনে রাখবেন যে আপনি যদি অনেকগুলি রূপান্তর করতে চলেছেন এবং আপনার এনামের অনেকগুলি সম্ভাব্য মান রয়েছে তবে এই সমাধানটি সিপিইউ নিবিড় হয়ে উঠতে পারে।


0

আমি কিছুটা দেরি করেছি তবে g ++ এবং কেবলমাত্র মানক লাইব্রেরি ব্যবহার করে আমার সমাধানটি এখানে দেওয়া হয়েছে। আমি নেমস্পেস দূষণ কমাতে এবং এনাম নামগুলি পুনরায় টাইপ করার যে কোনও প্রয়োজন মুছে ফেলার চেষ্টা করেছি।

"My_enum.hpp" শিরোনাম ফাইলটি হ'ল:

#include <cstring>

namespace ENUM_HELPERS{
    int replace_commas_and_spaces_with_null(char* string){
        int i, N;
        N = strlen(string);
        for(i=0; i<N; ++i){
            if( isspace(string[i]) || string[i] == ','){
                string[i]='\0';
            }
        }
        return(N);
    }

    int count_words_null_delim(char* string, int tot_N){
        int i;
        int j=0;
        char last = '\0';
        for(i=0;i<tot_N;++i){
            if((last == '\0') && (string[i]!='\0')){
                ++j;
            }
            last = string[i];
        }
        return(j);
    }

    int get_null_word_offsets(char* string, int tot_N, int current_w){
        int i;
        int j=0;
        char last = '\0';
        for(i=0; i<tot_N; ++i){
            if((last=='\0') && (string[i]!='\0')){
                if(j == current_w){
                    return(i);
                }
                ++j;
            }
            last = string[i];
        }
        return(tot_N); //null value for offset
    }

    int find_offsets(int* offsets, char* string, int tot_N, int N_words){
        int i;
        for(i=0; i<N_words; ++i){
            offsets[i] = get_null_word_offsets(string, tot_N, i);
        }
        return(0);
    }
}


#define MAKE_ENUM(NAME, ...)                                            \
namespace NAME{                                                         \
    enum ENUM {__VA_ARGS__};                                            \
    char name_holder[] = #__VA_ARGS__;                                  \
    int name_holder_N =                                                 \
        ENUM_HELPERS::replace_commas_and_spaces_with_null(name_holder); \
    int N =                                                             \
        ENUM_HELPERS::count_words_null_delim(                           \
            name_holder, name_holder_N);                                \
    int offsets[] = {__VA_ARGS__};                                      \
    int ZERO =                                                          \
        ENUM_HELPERS::find_offsets(                                     \
            offsets, name_holder, name_holder_N, N);                    \
    char* tostring(int i){                                              \
       return(&name_holder[offsets[i]]);                                \
    }                                                                   \
}

ব্যবহারের উদাহরণ:

#include <cstdio>
#include "my_enum.hpp"

MAKE_ENUM(Planets, MERCURY, VENUS, EARTH, MARS)

int main(int argc, char** argv){    
    Planets::ENUM a_planet = Planets::EARTH;
    printf("%s\n", Planets::tostring(Planets::MERCURY));
    printf("%s\n", Planets::tostring(a_planet));
}

এটি আউটপুট দেবে:

MERCURY
EARTH

আপনাকে কেবল একবার সমস্ত কিছু সংজ্ঞায়িত করতে হবে, আপনার নেমস্পেসটি দূষিত করা উচিত নয় এবং সমস্ত গণনা কেবল একবার করা হয় (বাকীটি কেবল লুকআপ)। তবে, আপনি এনাম শ্রেণীর টাইপ-সুরক্ষা পাবেন না (তারা এখনও কেবল সংক্ষিপ্ত পূর্ণসংখ্যার), আপনি এনামগুলিকে মান নির্ধারণ করতে পারবেন না, আপনাকে এনামগুলি কোথাও সংজ্ঞায়িত করতে হবে যেখানে আপনি নামের স্থান নির্ধারণ করতে পারবেন (যেমন বিশ্বব্যাপী)।

আমি নিশ্চিত নই যে এটিতে পারফরম্যান্সটি কতটা ভাল, বা যদি এটি একটি ভাল ধারণা (তবে আমি সি ++ এর আগে সি শিখেছিলাম তাই আমার মস্তিষ্ক এখনও সেভাবে কাজ করে)। যদি কেউ জানে যে এটি কেন একটি খারাপ ধারণা এটি নির্দ্বিধায় নির্দ্বিধায়।


0

এটি 2017 তবে প্রশ্নটি এখনও বেঁচে আছে

তবুও অন্য উপায়:

#include <iostream>

#define ERROR_VALUES \
ERROR_VALUE(NO_ERROR, 0, "OK") \
ERROR_VALUE(FILE_NOT_FOUND, 1, "Not found") \
ERROR_VALUE(LABEL_UNINITIALISED, 2, "Uninitialized usage")

enum Error
{
#define ERROR_VALUE(NAME, VALUE, TEXT) NAME = VALUE,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#define ERROR_VALUE(NAME, VALUE, TEXT) case NAME: return os << "[" << errVal << "]" << #NAME << ", " << TEXT;
    ERROR_VALUES
#undef ERROR_VALUE
    default:
        // If the error value isn't found (shouldn't happen)
        return os << errVal;
    }
}

int main() {
    std::cout << "Error: " << NO_ERROR << std::endl;
    std::cout << "Error: " << FILE_NOT_FOUND << std::endl;
    std::cout << "Error: " << LABEL_UNINITIALISED << std::endl;
    return 0;
}

আউটপুট:

Error: [0]NO_ERROR, OK
Error: [1]FILE_NOT_FOUND, Not found
Error: [2]LABEL_UNINITIALISED, Uninitialized usage

0
#pragma once

#include <string>
#include <vector>
#include <sstream>
#include <algorithm>

namespace StringifyEnum
{
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { ++rit; }
    return std::string(it, rit.base());
}

static std::vector<std::string> SplitEnumArgs(const char* szArgs, int     nMax)
{
    std::vector<std::string> enums;
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        enums.push_back(StringifyEnum::TrimEnumString(strSub));
        ++nIdx;
    }
    return std::move(enums);
}    
}

#define DECLARE_ENUM_SEQ(ename, n, ...) \
    enum class ename { __VA_ARGS__ }; \
    const int MAX_NUMBER_OF_##ename(n); \
    static std::vector<std::string> ename##Strings = StringifyEnum::SplitEnumArgs(#__VA_ARGS__, MAX_NUMBER_OF_##ename); \
    inline static std::string ename##ToString(ename e) { \
        return ename##Strings.at((int)e); \
    } \
    inline static ename StringTo##ename(const std::string& en) { \
        const auto it = std::find(ename##Strings.begin(), ename##Strings.end(), en); \
        if (it != ename##Strings.end()) \
            return (ename) std::distance(ename##Strings.begin(), it); \
        throw std::runtime_error("Could not resolve string enum value");     \
    }

এটি একটি বিস্তৃত শ্রেণীর বর্ধিত এনুম সংস্করণ ... এটি সরবরাহ করা মান ব্যতীত অন্য কোনও এনাম মান যোগ করে না।

ব্যবহার: DECLARE_ENUM_SEQ (ক্যামেরামোড, (3), ফ্লাই, ফার্স্ট পার্সন, পার্সিপেক্টিওক্র্যাক্ট)


0

উভয় দিকে কাজ করার জন্য আমার এটির প্রয়োজন ছিল এবং আমি প্রায়শই আমার এনামগুলিকে একটি সমন্বিত শ্রেণীর ভিতরে এম্বেড করেছিলাম এবং তাই আমি এই উত্তরগুলির শীর্ষে, জেমস ম্যাকনেলিসের মাধ্যমে সমাধানটি দিয়ে শুরু করেছিলাম, তবে আমি এই সমাধানটি করেছিলাম। নোট এছাড়াও আমি শুধু এনামের চেয়ে এনাম ক্লাস পছন্দ করি যা উত্তর কিছুটা জটিল করে তোলে।

#define X_DEFINE_ENUMERATION(r, datatype, elem) case datatype::elem : return BOOST_PP_STRINGIZE(elem);

// The data portion of the FOR_EACH should be (variable type)(value)
#define X_DEFINE_ENUMERATION2(r, dataseq, elem) \
    if (BOOST_PP_SEQ_ELEM(1, dataseq) == BOOST_PP_STRINGIZE(elem) ) return BOOST_PP_SEQ_ELEM(0, dataseq)::elem;

#define DEFINE_ENUMERATION_MASTER(modifier, name, toFunctionName, enumerators)    \
    enum class name {                                                         \
        Undefined,                                                            \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    modifier const char* ToString(const name & v)                               \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUMERATION,                                         \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }                                                                         \
                                                                              \
    modifier const name toFunctionName(const std::string & value)               \
    {                                                                         \
        BOOST_PP_SEQ_FOR_EACH(                                                \
            X_DEFINE_ENUMERATION2,                                            \
            (name)(value),                                                    \
            enumerators                                                       \
        )                                                                     \
        return name::Undefined;                                               \
    }

#define DEFINE_ENUMERATION(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(inline, name, toFunctionName, enumerators)

#define DEFINE_ENUMERATION_INSIDE_CLASS(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(static, name, toFunctionName, enumerators)

এটি কোনও শ্রেণীর অভ্যন্তরে ব্যবহার করতে, আপনি এরকম কিছু করতে পারেন:

class ComponentStatus {
public:
    /** This is a simple bad, iffy, and good status. See other places for greater details. */
    DEFINE_ENUMERATION_INSIDE_CLASS(Status, toStatus, (RED)(YELLOW)(GREEN)
}

এবং আমি একটি সিপিইউপিট পরীক্ষা লিখেছি, যা এটি কীভাবে ব্যবহার করতে হবে তা দেখায়:

void
ComponentStatusTest::testSimple() {
    ComponentStatus::Status value = ComponentStatus::Status::RED;

    const char * valueStr = ComponentStatus::ToString(value);

    ComponentStatus::Status convertedValue = ComponentStatus::toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

DEFINE_ENUMERATION(Status, toStatus, (RED)(YELLOW)(GREEN))

void
ComponentStatusTest::testOutside() {
    Status value = Status::RED;

    const char * valueStr = ToString(value);

    Status convertedValue = toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

আপনাকে কোন ম্যাক্রো ব্যবহার করতে হবে তা বেছে নিতে হবে, হয় DEFINE_ENUMERATION বা DEFINE_ENUMERATION_INSIDE_CLASS। আপনি দেখতে পাবেন যে আমি কম্পোনেন্ট স্ট্যাটাস :: স্থিতি সংজ্ঞায়িত করার সময়কারটি ব্যবহার করেছি তবে কেবলমাত্র স্থিতি সংজ্ঞায়িত করার সময় আমি পূর্বটিকে ব্যবহার করেছি। পার্থক্যটি সহজ। কোনও শ্রেণীর অভ্যন্তরে, আমি "স্থিতিশীল" হিসাবে / থেকে পদ্ধতিগুলিকে উপসর্গ করি এবং কোনও শ্রেণিতে না থাকলে আমি "ইনলাইন" ব্যবহার করি। তুচ্ছ পার্থক্য, তবে প্রয়োজনীয়।

দুর্ভাগ্যক্রমে, আমি মনে করি না যে এটি করার এড়ানোর একটি পরিষ্কার উপায় আছে:

const char * valueStr = ComponentStatus::ToString(value);

যদিও আপনি নিজের শ্রেণীর সংজ্ঞার পরে ম্যানুয়ালি একটি ইনলাইন পদ্ধতি তৈরি করতে পেরেছিলেন যা ক্লাস পদ্ধতিতে কেবল চেইন করে, এরকম কিছু:

inline const char * toString(const ComponentStatus::Status value) { return ComponentStatus::ToString(value); }

0

আমার নিজের উত্তর, বুস্ট ব্যবহার না করে - ভারী সংজ্ঞায়িত যাদু ছাড়াই আমার নিজস্ব পদ্ধতির ব্যবহার এবং এই সমাধানটির নির্দিষ্ট এনাম মান নির্ধারণ করতে সক্ষম না হওয়ার একটি সীমাবদ্ধতা রয়েছে।

#pragma once
#include <string>

template <class Enum>
class EnumReflect
{
public:
    static const char* getEnums() { return ""; }
};

#define DECLARE_ENUM(name, ...)                                         \
    enum name { __VA_ARGS__ };                                          \
    template <>                                                         \
    class EnumReflect<##name> {                                         \
    public:                                                             \
        static const char* getEnums() { return #__VA_ARGS__; }          \
    };

/*
    Basic usage:

    Declare enumeration:

DECLARE_ENUM( enumName,

    enumValue1,
    enumValue2,
    enumValue3,

    // comment
    enumValue4
);

    Conversion logic:

    From enumeration to string:

        printf( EnumToString(enumValue3).c_str() );

    From string to enumeration:

       enumName value;

       if( !StringToEnum("enumValue4", value) )
            printf("Conversion failed...");

    WARNING: At the moment assigning enum value to specific number is not supported.
*/

//
//  Converts enumeration to string, if not found - empty string is returned.
//
template <class T>
std::string EnumToString(T t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = (int)t;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (id == 0)
            return std::string(token, next);
        id--;
    } while (*next != 0);

    return std::string();
}

//
//  Converts string to enumeration, if not found - false is returned.
//
template <class T>
bool StringToEnum(const char* enumName, T& t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = 0;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (strncmp(token, enumName, next - token) == 0)
        {
            t = (T)id;
            return true;
        }

        id++;
    } while (*next != 0);

    return false;
}

সর্বশেষ সংস্করণটি এখানে গিথুবে পাওয়া যাবে:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h


0

এর আরও অনেক উত্তর রয়েছে তবে আমি মনে করি এর থেকে আরও ভাল উপায় হ'ল সি ++ 17 বৈশিষ্ট্যগুলি ব্যবহার করা এবং কনটেক্সটপ্র ব্যবহার করা যাতে অনুবাদগুলি সংকলনের সময় করা হয়। এটি টাইপ নিরাপদ এবং ম্যাক্রোগুলি নিয়ে আমাদের গণ্ডগোলের দরকার নেই। নিচে দেখ:

//enum.hpp
#include <array>
#include <string_view>

namespace Enum
{

template <class ENUM_TYPE, size_t SIZE>
constexpr ENUM_TYPE findKey(const char * value, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Value not in map":
        (std::string_view(map[index - 1].second) == value) ? map[index- 1].first:
        findKey(value, map, index - 1);
};

template <class ENUM_TYPE, size_t SIZE>
constexpr const char * findValue(ENUM_TYPE key, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Key not in map":
        (map[index - 1].first == key) ? map[index- 1].second:
        findValue(key, map, index - 1);
};

}

//test_enum.hpp
#include "enum.hpp"

namespace TestEnum
{
    enum class Fields
    {
        Test1,
        Test2,
        Test3,
        //This has to be at the end
        NUMBER_OF_FIELDS
    };

    constexpr std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> GetMap()
    {
        std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> map =
        {
            {
                    {Fields::Test1, "Test1"},
                    {Fields::Test2, "Test2"},
                    {Fields::Test3, "Test3"},
            }
        };
        return map;
    };

    constexpr Fields StringToEnum(const char * value)
    {
        return Enum::findKey(value, GetMap());
    }

    constexpr const char * EnumToString(Fields key)
    {
        return Enum::findValue(key, GetMap());
    }

}

এটি তখন সহজেই ব্যবহার করা যায় যাতে সংকলনের সময় স্ট্রিং কী ত্রুটিগুলি সনাক্ত করা যায়:

#include "test_enum.hpp"

int main()
{
    auto constexpr a = TestEnum::StringToEnum("Test2"); //a = TestEnum::Fields::Test2
    auto constexpr b = TestEnum::EnumToString(TestEnum::Fields::Test1); //b = "Test1"
    auto constexpr c = TestEnum::StringToEnum("AnyStringNotInTheMap"); //compile time failure
    return 0;
}

কোডটি অন্য কয়েকটি সমাধানগুলির চেয়ে আরও ভার্বোজ তবে আমরা সহজেই সংকলনের সময় এনাম থেকে স্ট্রিং রূপান্তর এবং স্ট্রিং টু এনাম রূপান্তর করতে পারি এবং টাইপ ত্রুটিগুলি সনাক্ত করতে পারি। ভবিষ্যতের কয়েকটি C ++ 20 বৈশিষ্ট্যের সাথে এটি সম্ভবত আরও কিছুটা সরল করা যেতে পারে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.