2013-02-11 15 views
7

अगर मैं एक enum की तुलना में अधिक है, उदाहरण के लिए:सी में टाइपएफ़ enums?

enum Greetings{ hello, bye, how }; 

enum Testing { one, two, three }; 

मैं सही enum के उपयोग कैसे लागू कर सकते हैं? उदाहरण के लिए, मैं नहीं चाहता कि कोई hello का उपयोग करे, जब उन्हें बेहतर डिबगिंग और पठनीयता के लिए one का उपयोग करना चाहिए।

उत्तर

9

सी में, आप इसे बॉयलरप्लेट कोड के साथ नकली बना सकते हैं।

typedef enum { HELLO_E, GOODBYE_E } greetings_t; 
struct greetings { greetings_t greetings; }; 
#define HELLO ((struct greetings){HELLO_E}) 
#define GOODBYE ((struct greetings){GOODBYE_E}) 

typedef enum { ONE_E, TWO_E } number_t; 
struct number { number_t number; }; 
#define ONE ((struct number){ONE_E}) 
#define TWO ((struct number){TWO_E}) 

void takes_greeting(struct greetings g); 
void takes_number(struct number n); 

void test() 
{ 
    takes_greeting(HELLO); 
    takes_number(ONE); 

    takes_greeting(TWO); 
    takes_number(GOODBYE); 
} 

यह किसी भी भूमि के ऊपर उठाना चाहिए नहीं है, और त्रुटियों का उत्पादन बजाय चेतावनी की:

 
$ gcc -c -std=c99 -Wall -Wextra test2.c 
test2.c: In function ‘test’: 
test2.c:19: error: incompatible type for argument 1 of ‘takes_greeting’ 
test2.c:20: error: incompatible type for argument 1 of ‘takes_number’ 

सूचना है कि मैं जीएनयू एक्सटेंशन का उपयोग नहीं कर रहा हूँ, और कोई नकली चेतावनी उत्पन्न कर रहे हैं। केवल त्रुटियांयह भी ध्यान रखें कि मैं गंदगी के रूप में ही पुराना है कि जीसीसी के एक संस्करण,

 
$ gcc --version 
powerpc-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5493) 
Copyright (C) 2005 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

यह C99 के परिसर शाब्दिक लिए समर्थन के साथ किसी भी संकलक साथ काम करना चाहिए उपयोग कर रहा हूँ।

+1

यह साफ है, लेकिन स्विच स्टेटमेंट्स में एम्स के साथ थोड़ा बदसूरत काम करता है। – Toby

-2

आप अपने enums टाइप कर सकते हैं और फिर उन प्रकार के चर और समारोह तर्क घोषित कर सकते हैं।

+0

जीसीसी कि बेकार है में। – LtWorf

8

क्लैंग निम्न चेतावनी उत्पन्न करता है, जो आप कर सकते हैं सबसे अच्छा है (हालांकि उपयोगकर्ता चेतावनी को किसी त्रुटि में अपग्रेड कर सकता है)।

enum Greetings { hello, bye, how }; 
enum Count { one, two, three }; 

void takes_greeting(enum Greetings x) {} 
void takes_count(enum Count x) {} 

int main() { 
    takes_greeting(one); 
    takes_count(hello); 
} 

संकलक उत्पादन:

cc  foo.c -o foo 
foo.c:8:17: warning: implicit conversion from enumeration type 'enum Count' to different enumeration type 'enum Greetings' [-Wenum-conversion] 
     takes_greeting(one); 
     ~~~~~~~~~~~~~~ ^~~ 
foo.c:9:14: warning: implicit conversion from enumeration type 'enum Greetings' to different enumeration type 'enum Count' [-Wenum-conversion] 
     takes_count(hello); 
     ~~~~~~~~~~~ ^~~~~ 

उपयोगकर्ताओं संकलक से त्रुटियों और चेतावनियों की अनदेखी करने के लिए जा रहे हैं, तो काफी नहीं है कि आप उन्हें मदद करने के लिए कर सकते हैं।

+2

अफसोस की बात है, हालांकि, जीसीसी सी मोड में एक समान चेतावनी उत्पन्न नहीं करता है। मुझे नहीं पता कि एमएसवीसी करता है या नहीं करता है। कुछ टिप्पणियां हैं [यहां] (http://stackoverflow.com/questions/4669454/how-to-make-gcc-warn-about-passing-wrong-enum-to-a- कार्यक्षमता) जीसीसी को कैसे चालित करें पठनीयता की लागत पर चेतावनी का उत्पादन। –

2

यह वह उत्तर है जिसे आप सुनना नहीं चाहते हैं। सी में, आप वास्तव में नहीं कर सकते हैं। अब यदि आपका सी कोड सी ++ के "क्लीन सी" सबसेट में था, तो आप गलत एनम/इंट वैल्यू इत्यादि के सभी त्रुटियों को प्राप्त करने के लिए सी ++ कंपाइलर के साथ संकलित कर सकते हैं।

+0

सी ++ में भी यह सीधा नहीं है: आपको सी ++ 11 "एनम क्लास" सुविधा की आवश्यकता है, और फिर हर जगह कक्षा का नाम निर्दिष्ट करने के लिए - उदा। 'foo = ग्रीटिंग्स :: हैलो'; – Roddy

2

दुर्भाग्य से enum एक कमजोर बिंदु है enum प्रकार के सी चर के प्रकार सिस्टम enum प्रकार के हैं, लेकिन enum के साथ घोषित स्थिरांक int प्रकार हैं।

अपने उदाहरण में

तो

enum Greetings{ hello, bye, how }; 
enum Testing { one, two, three }; 

enum Greetings const holla = hello; 
enum Testing const eins = one; 

hello और one एक ही मूल्य, अर्थात् 0 के लिए दो नाम हैं, और एक ही प्रकार int साथ।

holla और eins फिर से 0 मानते हैं, लेकिन उनके संबंधित प्रकार हैं।

#define GREETING(VAL) ((enum Greetings){ 0 } = (VAL)) 
#define HELLO GREETING(hello) 
:

आप "असली" स्थिरांक के लिए कुछ "आधिकारिक" प्रकार सुरक्षा बल चाहते हैं, कि संस्थाओं प्रकार और मान जो आप चाहते है कि है, तो आप कुछ और अधिक शामिल निर्माणों का उपयोग करना होगा

GREETING मैक्रो में असाइनमेंट सुनिश्चित करता है कि परिणाम एक "रावल्यू" है, इसलिए इसे संशोधित नहीं किया जा सकता है और केवल इसके प्रकार और मूल्य के लिए कंपाइलर द्वारा लिया जाएगा।

1

यदि आप वैध सीमा सुनिश्चित करना चाहते हैं, तो एक तकनीक है जो पूर्णांक मान प्राप्त करने के लिए पॉइंटर अव्यवस्था के छोटे ओवरहेड के साथ आता है - और बहुत सारे बॉयलरप्लेट टाइपिंग। यह अभी भी उपयोगी हो सकता है क्योंकि यह आपको रेंज-चेकिंग कोड लिखने से लाभ देता है जहां यह अन्यथा आवश्यक होगा।

greetings.h:

#ifndef GREETINGS_H 
#define GREETINGS_H 

struct greetings; 
typedef struct greetings Greetings; 

extern const Greetings * const Greetings_hello; 
extern const Greetings * const Greetings_bye; 
extern const Greetings * const Greetings_how; 

const char *Greetings_str(const Greetings *g); 
int Greetings_int(const Greetings *g); 

#endif 

greetings.c:

#include "greetings.h" 

struct greetings { 
    const int val; 
}; 

static const Greetings hello = { 0 }; 
static const Greetings bye = { 1 }; 
static const Greetings how = { 2 }; 

const Greetings * const Greetings_hello = &hello; 
const Greetings * const Greetings_bye = &bye; 
const Greetings * const Greetings_how = &how; 

static const char * const Greetings_names[] = { 
    "hello", 
    "bye", 
    "how" 
}; 

const char * 
Greetings_str(const Greetings *g) 
{ 
    return Greetings_names[g->val]; 
} 

int 
Greetings_int(const Greetings *g) 
{ 
    return g->val; 
} 

उदाहरण main.c:

#include <stdio.h> 
#include "greetings.h" 

void 
printTest(const Greetings *greeting) 
{ 
    if (greeting == Greetings_how) return; 
    puts(Greetings_str(greeting)); 
} 

int 
main() 
{ 
    const Greetings *g = Greetings_hello; 
    printTest(g); 
} 

हाँ, बहुत टाइप करने के लिए है, लेकिन आप पूर्ण प्रकार मिलता है और सीमा सुरक्षा। कोई अन्य संकलन इकाई कभी भी struct greetings को तुरंत चालू नहीं कर सकती है, इसलिए आप पूरी तरह से सुरक्षित हैं।


संपादित करें 2015-07-04: न्यूल के खिलाफ सुरक्षा के लिए, दो संभावनाएं हैं। या तो डिफ़ॉल्ट मान के लिए NULL का उपयोग करें (अब उपयोग किए गए सूचक के बजाय #define Greetings_hello 0)। यह बहुत सुविधाजनक है, लेकिन डिफॉल्ट एनम वैल्यू के लिए टाइप सुरक्षा को छोड़ देता है, एनयूएलएल किसी भी एनम के लिए इस्तेमाल किया जा सकता है। या यह अमान्य घोषित करने और या तो एक्सेसर तरीकों में इसके लिए जाँच, एक त्रुटि लौटने, या संकलन समय, जैसे में greetings.h पर यह पकड़ने के लिए GCCs __attribute__((nonnull())) की तरह कुछ का उपयोग करें:

const char *Greetings_str(const Greetings *g) 
     __attribute__((nonnull(1))); 
+0

यदि उपयोगकर्ता 'शून्य' में गुज़र रहा है, तो जितनी जल्दी हो सके क्रैश करना सबसे अच्छा है और उन्हें इसे हल करने दें। या, जैसा कि आप कहते हैं, एक कंपाइलर-विशिष्ट चाल का उपयोग करें। एक छोटा सुधार मैं सुझाव दे सकता हूं कि ग्रीटिंग स्ट्रिंग को 'ग्रीटिंग्स' स्ट्रक्चर में ले जाएं, क्योंकि आप उन्हें उदाहरण के सदस्यों की तरह व्यवहार कर रहे हैं। तो अगर आप उदा। 'स्थिर कॉन्स ग्रीटिंग्स हैलो = {0," हैलो "}; ', और इसी तरह, आप कार्यान्वयन में' जी-> लेबल 'वापस कर सकते हैं। – Yawar

संबंधित मुद्दे