क्या सी + + एनम वर्ग के तत्वों की संख्या निर्धारित करना संभव है?


85

क्या सी + + की कार्डिनैलिटी निर्धारित करना संभव है enum class:

enum class Example { A, B, C, D, E };

मैंने उपयोग करने की कोशिश की sizeof, हालांकि, यह एक एनम तत्व का आकार देता है।

sizeof(Example); // Returns 4 (on my architecture)

क्या कार्डिनैलिटी (मेरे उदाहरण में 5) पाने का एक मानक तरीका है?


मैंने सोचा कि वहाँ एक विशिष्ट c ++ 11 तंत्र हो सकता है
bquenin

6
यह कोई डुप्लिकेट नहीं है, वैसे। enumऔर enum classes बहुत अलग अवधारणाएँ हैं।
जूता

@ शो ... वे वास्तव में हैं, हालांकि?
काइल स्ट्रैंड

1
यह एक XY समस्या की तरह लगता है, मुझे पता है कि यह बहुत पहले से था, लेकिन क्या आपको याद है कि आपको ऐसा करने की आवश्यकता क्यों थी? आप enum classमानों पर पुनरावृत्ति नहीं कर सकते , इसलिए नंबर जानने के लिए क्या होगा?
शानदार मिस्टर फॉक्स

जवाबों:


71

सीधे नहीं, लेकिन आप निम्नलिखित चाल का उपयोग कर सकते हैं:

enum class Example { A, B, C, D, E, Count };

फिर कार्डिनैलिटी के रूप में उपलब्ध है static_cast<int>(Example::Count)

बेशक, यह केवल अच्छी तरह से काम करता है यदि आप एनम के मूल्यों को स्वचालित रूप से असाइन करने देते हैं, 0. से शुरू। यदि ऐसा नहीं है, तो आप मैन्युअल रूप से गणना के लिए सही कार्डिनैलिटी असाइन कर सकते हैं, जो वास्तव में एक अलग निरंतर बनाए रखने से अलग नहीं है। वैसे भी:

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };

एक नुकसान यह है कि संकलक आपको Example::Countएक एनम मान के तर्क के रूप में उपयोग करने की अनुमति देगा - इसलिए यदि आप इसका उपयोग करते हैं तो सावधान रहें! (मैं व्यक्तिगत रूप से इसे अभ्यास में समस्या नहीं मानता, हालांकि।)


1
Enum मान एक Enum वर्ग में सुरक्षित होते हैं, इसलिए 'Count' यहां उदाहरण के लिए होगा और इंट, राइट नहीं होगा? आकार के लिए इसका उपयोग करने के लिए आपको पहले एक इंट में 'काउंट' डालना होगा।
मैन ऑफ वन वे

@ मैन: हां, यह ट्रिक enum classप्लेन एस के बजाय एस के साथ थोड़ी गड़बड़ है enum। मैं स्पष्ट होने के लिए एक कास्ट में संपादित करूंगा।
कैमरून

11
यदि आप इस एनम के साथ एक स्विच स्टेटमेंट का उपयोग करते हैं, तो कोई भी सभ्य संकलक आपको चेतावनी देगा कि आप एक मामले को याद कर रहे हैं। यदि यह बहुत अधिक उपयोग किया जाता है जो बहुत कष्टप्रद हो सकता है .. तो इस विशिष्ट मामले में एक अलग चर के लिए बेहतर हो सकता है।
शानदार मिस्टर फॉक्स

@FantasticMrFox मैं अनुभव के आधार पर 100% सहमत हूं। यह संकलक चेतावनी एक महत्वपूर्ण भी है। मैंने एक वैकल्पिक दृष्टिकोण पोस्ट किया है, जो आपकी सिफारिश की भावना के अनुरूप है।
arr_sea

26

C ++ 17 के लिए आप magic_enum::enum_countlib https://github.com/Neargye/magic_enum से उपयोग कर सकते हैं :

magic_enum::enum_count<Example>() -> ४।

कमी कहां है?

यह लाइब्रेरी कंपाइलर-विशिष्ट हैक ( __PRETTY_FUNCTION__/ के आधार पर __FUNCSIG__) का उपयोग करती है, जो Clang> = 5, MSVC> = 15.3 और GCC> = 9 पर काम करती है।

हम दिए गए अंतराल सीमा से गुजरते हैं, और एक नाम के साथ सभी गणनाओं को पाते हैं, यह उनकी गिनती होगी। सीमाओं के बारे में और पढ़ें

इस पोस्ट में इस हैक के बारे में और भी कई https://taylorconor.com/blog/enum-reflection


2
यह कमाल का है! एनम सदस्यों की संख्या की गणना करने के लिए मौजूदा कोड को संशोधित करने की आवश्यकता नहीं है। इसके अलावा यह बहुत ही सुरुचिपूर्ण ढंग से लागू किया गया है (बस कोड के माध्यम से स्किम्ड)!
और्री जूल

लिंक-केवल उत्तर आमतौर पर हतोत्साहित किए जाते हैं। क्या आप अपनी लाइब्रेरी द्वारा उपयोग की जाने वाली तकनीक के विवरण के साथ इसका विस्तार कर सकते हैं?
एड्रियन मैक्कार्थी

24
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one 
    ONE = 7
  , TWO = 6
  , THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;

यह UglyCoder के उत्तर से लिया गया है, लेकिन इसे तीन तरीकों से सुधारता है।

  • Type_safe enum ( BEGINऔर SIZE) में कोई अतिरिक्त तत्व नहीं हैं ( कैमरन के जवाब में भी यह समस्या है।)
    • कंपाइलर स्विच स्टेटमेंट (एक महत्वपूर्ण समस्या) से उनके गायब होने की शिकायत नहीं करेगा
    • वे अनजाने में आपके enum की अपेक्षा वाले कार्यों में पारित नहीं हो सकते। (सामान्य समस्या नहीं है)
  • इसे उपयोग के लिए कास्टिंग की आवश्यकता नहीं है। ( कैमरन के जवाब में भी यह समस्या है।)
  • एन्युम वर्ग प्रकार के आकार के साथ घटाव गड़बड़ नहीं करता है।

यह कैमरून के जवाब पर UglyCoder के लाभ को बरकरार रखता है कि प्रगणकों को मनमाना मूल्य सौंपा जा सकता है।

एक समस्या ( UglyCoder के साथ लेकिन कैमरन के साथ साझा नहीं ) यह है कि यह नई कहानियों और टिप्पणियों को महत्वपूर्ण बनाती है ... जो अप्रत्याशित है। तो कोई व्हाट्सएप के साथ एक प्रविष्टि जोड़ सकता है या एक टिप्पणी को समायोजित किए बिना कर सकता है TEST_SIZE


7
enum class TEST
{
    BEGIN = __LINE__
    , ONE
    , TWO
    , NUMBER = __LINE__ - BEGIN - 1
};

auto const TEST_SIZE = TEST::NUMBER;

// or this might be better 
constexpr int COUNTER(int val, int )
{
  return val;
}

constexpr int E_START{__COUNTER__};
enum class E
{
    ONE = COUNTER(90, __COUNTER__)  , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;

चालाक! बेशक, कोई टिप्पणी या असामान्य रिक्ति नहीं हो सकती है, और वास्तव में बड़े स्रोत फ़ाइलों के लिए, अंतर्निहित मूल्य प्रकार की तुलना में बड़ा हो सकता है अन्यथा यह होगा।
काइल स्ट्रैंड

@ काइल स्ट्रैंड: वहाँ यह मुद्दा है: चार का उपयोग करना और आपके पास 256 से अधिक प्रगणक भी हैं। लेकिन संकलक के पास आपको ट्रंकेशन आदि को सूचित करने के लिए अच्छे शिष्टाचार हैं। LINE एक पूर्णांक शाब्दिक है और # लाइन का उपयोग करने की सीमा [1, 2147483647] है
UglyCoder

आह ठीक है। फिर भी, यहां तक ​​कि एक एनुम का निर्माण किया जा सकता है, जो कि अन्यथा, उदाहरण के shortलिए टकरा सकता है int। (मैं कहता हूँ कि यह एकता के साथ एक समस्या का अधिक है, आपके प्रस्तावित चाल के साथ की तुलना में बनाता है।)
काइल स्ट्रैंड

छल? :-) मैं इसका उपयोग करता हूं, लेकिन शायद ही कभी और उचित निर्णय के साथ। कोडिंग में सब कुछ की तरह हमें पेशेवरों और विपक्षों और विशेष रूप से दीर्घकालिक रखरखाव निहितार्थों को बनाने की आवश्यकता है। मैंने हाल ही में इसका उपयोग C #defines (OpenGL wglExt.h) की सूची से एनम वर्ग बनाने के लिए किया है।
अग्लीकोडर 5

5

X () - मैक्रोज़: छवि पर आधारित एक चाल है, आपके पास निम्न एनम है:

enum MyEnum {BOX, RECT};

इसे सुधारें:

#define MyEnumDef \
    X(BOX), \
    X(RECT)

फिर निम्न कोड एनम प्रकार को परिभाषित करता है:

enum MyEnum
{
#define X(val) val
    MyEnumDef
#undef X
};

और निम्नलिखित कोड एनम तत्वों की संख्या की गणना करता है:

template <typename ... T> void null(T...) {}

template <typename ... T>
constexpr size_t countLength(T ... args)
{
    null(args...); //kill warnings
    return sizeof...(args);
}

constexpr size_t enumLength()
{
#define XValue(val) #val
    return countLength(MyEnumDef);
#undef XValue
}

...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...

यह कॉमा को #define MyEnumDef(और इसे डालकर #define X(val) val) को हटाकर आसान बनाया जा सकता है , जो आपको बस उपयोग करने वाले तत्वों की संख्या की गणना करने की अनुमति देता है #define X(val) +1 constexpr std::size_t len = MyEnumDef;
पवित्रब्लैकैट

4

एक ट्रिक जो आप आजमा सकते हैं, वह है अपनी सूची के अंत में एक एनम वैल्यू जोड़ना और आकार के रूप में उपयोग करना। अपने उदाहरण में

enum class Example { A, B, C, D, E, ExampleCount };

सादे enumएस के साथ व्यवहार की तुलना में , यह काम नहीं करेगा ExampleCountजैसा कि प्रकार का है Example। तत्वों की संख्या प्राप्त करने के लिए Example, ExampleCountपूर्णांक प्रकार पर कास्ट करना होगा।
सेब का छिलका

3

यदि आप बूस्ट के प्रीप्रोसेसर उपयोगिताओं का उपयोग करते हैं, तो आप का उपयोग करके गणना प्राप्त कर सकते हैं BOOST_PP_SEQ_SIZE(...)

उदाहरण के लिए, CREATE_ENUMमैक्रो को निम्नानुसार परिभाषित किया जा सकता है:

#include <boost/preprocessor.hpp>

#define ENUM_PRIMITIVE_TYPE std::int32_t

#define CREATE_ENUM(EnumType, enumValSeq)                                  \
enum class EnumType : ENUM_PRIMITIVE_TYPE                                  \
{                                                                          \
   BOOST_PP_SEQ_ENUM(enumValSeq)                                           \
};                                                                         \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count =                     \
                 BOOST_PP_SEQ_SIZE(enumValSeq);                            \
// END MACRO   

फिर, मैक्रो को कॉल करना:

CREATE_ENUM(Example, (A)(B)(C)(D)(E));

निम्नलिखित कोड उत्पन्न करेगा:

enum class Example : std::int32_t 
{
   A, B, C, D, E 
};
static constexpr std::int32_t ExampleCount = 5;

यह केवल बूस्ट प्रीप्रोसेसर टूल्स के संबंध में सतह को खरोंच कर रहा है। उदाहरण के लिए, आपका मैक्रो आपके दृढ़ता से टाइप किए गए एनम के लिए स्ट्रिंग रूपांतरण उपयोगिताओं और ओस्ट्रीम ऑपरेटरों से / को भी परिभाषित कर सकता है।

यहाँ पर प्रीप्रोसेसर उपकरण को बढ़ावा देने के लिए और अधिक: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessMetaprogramming.html


एक तरफ के रूप में, मैं @FantasticMrFox के साथ दृढ़ता से सहमत हूं कि Countस्वीकृत उत्तर में नियोजित अतिरिक्त प्रगणित मान एक switchबयान का उपयोग करते हुए संकलक चेतावनी सिरदर्द बनाएगा । मुझे लगता है कि unhandled caseसंकलक चेतावनी सुरक्षित कोड रखरखाव के लिए काफी उपयोगी है, इसलिए मैं इसे कम नहीं करना चाहूंगा।


@FantasticMrFox स्वीकृत उत्तर के साथ एक समस्या के बारे में बताने के लिए धन्यवाद। मैंने यहां एक वैकल्पिक दृष्टिकोण प्रदान किया है जो आपकी सिफारिश की भावना के अनुरूप है।
arr_sea

2

इसे std :: initializer_list के साथ चाल द्वारा हल किया जा सकता है:

#define TypedEnum(Name, Type, ...)                                \
struct Name {                                                     \
    enum : Type{                                                  \
        __VA_ARGS__                                               \
    };                                                            \
    static inline const size_t count = []{                        \
        static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
    }();                                                          \
};

उपयोग:

#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)

int main()
{
    std::cout << FakeEnum::A     << std::endl
              << FakeEnun::count << std::endl;
}

2

एक और तरीका है जो लाइन काउंट या टेम्प्लेट पर निर्भर नहीं करता है। केवल आवश्यकता अपनी ही फाइल में एनम मानों को चिपका रही है और प्रीप्रोसेसर / कंपाइलर की गिनती इस तरह से कर रही है:

my_enum_inc.h

ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL

my_enum.h

typedef enum {
  #define ENUMVAL(TYPE) TYPE,
  #include "my_enum_inc.h"
} Fruits;

#define ENUMVAL(TYPE) +1
const size_t num_fruits =
  #include "my_enum_inc.h"
  ;

यह आपको एनम मानों के साथ टिप्पणियां डालने, मूल्यों को फिर से असाइन करने की अनुमति देता है और एक अमान्य 'गणना' एनम वैल्यू को इंजेक्ट नहीं करता है जिसे कोड में नजरअंदाज / हिसाब करने की आवश्यकता होती है।

यदि आपको उन टिप्पणियों की परवाह नहीं है, जिनके लिए आपको एक अतिरिक्त फ़ाइल की आवश्यकता नहीं है, और जैसा कि ऊपर वर्णित किसी व्यक्ति ने किया है, जैसे:

#define MY_ENUM_LIST \
    ENUMVAL(BANANA) \
    ENUMVAL(ORANGE = 7) \
    ENUMVAL(KIWI)

और #include "my_enum_inc.h"निर्देशों को MY_ENUM_LIST से प्रतिस्थापित करें लेकिन आपको #undef ENUMVALप्रत्येक उपयोग के बाद की आवश्यकता होगी ।


1

इसका एक और प्रकार "बेवकूफ" समाधान है:

enum class Example { A, B, C, D, E };

constexpr int ExampleCount = [] {
  Example e{};
  int count = 0;
  switch (e) {
    case Example::A:
      count++;
    case Example::B:
      count++;
    case Example::C:
      count++;
    case Example::D:
      count++;
    case Example::E:
      count++;
  }

  return count;
}();

इसे संकलित करके -Werror=switchआप संकलक चेतावनी प्राप्त करना सुनिश्चित करते हैं यदि आप किसी स्विच मामले को छोड़ देते हैं या उसकी नकल करते हैं। यह भी कमी है तो यह संकलन समय पर गणना की जाती है।

लेकिन ध्यान दें कि एक एन enum classके लिए भी डिफॉल्ट इनिशियलाइज्ड वैल्यू 0 है, भले ही एनम का पहला मूल्य 0. नहीं है। इसलिए आपको या तो 0 पर शुरू करना होगा या स्पष्ट रूप से पहले मूल्य का उपयोग करना होगा।


0

नहीं, आपको इसे कोड में लिखना होगा।


0

आप यह भी विचार कर सकते हैं static_cast<int>(Example::E) + 1जो अतिरिक्त तत्व को समाप्त करता है।


8
यह जवाब इस विशिष्ट प्रोग्रामिंग समस्या के लिए सही है, लेकिन सामान्य रूप से सुरुचिपूर्ण और त्रुटि प्रवणता से दूर है। एनम को भविष्य में नए मूल्यों के साथ बढ़ाया जा सकता Example::Eहै जो एनम में अंतिम मान के रूप में बदल सकते हैं । अगर ऐसा नहीं है, तो भी Example::Eशाब्दिक मूल्य बदल सकता है।
मथायस

0

परावर्तन टीएस: एनमों का स्थिर प्रतिबिंब (और अन्य प्रकार)

परावर्तन TS , विशेष रूप से [ प्रतिबिंबित. ops.enum] / परावर्तन TS ड्राफ्ट के नवीनतम संस्करण का 2 get_enumerators TransformationTraitऑपरेशन प्रदान करता है :

[Reflect.ops.enum] / 2

template <Enum T> struct get_enumerators

आवश्यकताओं के सभी विशेषज्ञताओं get_enumerators<T>को पूरा TransformationTraitकरना होगा (20.10.1)। नेस्टेड प्रकार नामित typeमेटा-ऑब्जेक्ट प्रकार को संतुष्ट करता है ObjectSequence, जिसमें ऐसे तत्व होते हैं जो संतुष्ट करते हैं Enumeratorऔर परिलक्षित प्रकार के एन्यूमरेटर्स को प्रतिबिंबित करते हैं T

[परावर्तित करें। ड्राफ्ट.ओबसेक] ड्राफ्ट के ObjectSequenceसंचालन को शामिल करता है, जहां विशेष रूप से [परावर्तन। ऑबजसेक] / 1 get_sizeमेटा-ऑब्जेक्ट संतोषजनक के लिए तत्वों की संख्या निकालने के लिए विशेषता को शामिल करता है ObjectSequence:

[Reflect.ops.objseq] / 1

template <ObjectSequence T> struct get_size;

सभी विशेषताओं को आधार विशेषता के साथ आवश्यकताओं (20.10.1) get_size<T>को पूरा करना होगा , जहां ऑब्जेक्ट अनुक्रम में तत्वों की संख्या है।UnaryTypeTraitintegral_constant<size_t, N>N

इस प्रकार, परावर्तन टीएस को अपने वर्तमान रूप में स्वीकार और कार्यान्वित किया जाना था, एक एनुम के तत्वों की संख्या की गणना, संकलन समय पर, निम्नानुसार की जा सकती है:

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators<Example>::type;

static_assert(get_size<ExampleEnumerators>::value == 5U, "");

जहाँ हम अन्य उपनामों को देखने get_enumerators_vऔर get_type_vआगे प्रतिबिंब को सरल बनाने की संभावना रखते हैं :

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators_t<Example>;

static_assert(get_size_v<ExampleEnumerators> == 5U, "");

परावर्तन TS पर स्थिति

जैसा कि हर्ब सटर की ट्रिप रिपोर्ट: समर ISO C ++ मानकों की बैठक (रैपरस्विल) 9 जून, 2018 से आईएसओ C ++ कमेटी की समर मीटिंग, रिफ्लेक्शन टीएस को फीचर-पूर्ण घोषित किया गया है

परावर्तन टीएस सुविधा-पूर्ण है : परावर्तन टीएस को फीचर-पूर्ण घोषित किया गया था और इसे गर्मियों में मुख्य टिप्पणी मतपत्र के लिए भेजा जा रहा है। फिर से ध्यान दें कि टीएस का वर्तमान टेम्पलेट मेटाप्रोग्रामिंग-आधारित सिंटैक्स सिर्फ एक प्लेसहोल्डर है; अनुरोध की जा रही प्रतिक्रिया डिजाइन के मूल "हिम्मत" पर है, और समिति पहले से ही यह जानती है कि सतह के सिंटैक्स को एक सरल प्रोग्रामिंग मॉडल के साथ बदलने का इरादा है जो साधारण संकलन-समय कोड और <>नॉट -स्टाइल मेटाप्रोग्रामिंग का उपयोग करता है ।

और शुरू में C ++ 20 के लिए योजना बनाई गई थी, लेकिन यह कुछ हद तक स्पष्ट नहीं है अगर प्रतिबिंब TS के पास अभी भी C ++ 20 रिलीज में बनाने का मौका होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.