2012-02-22 21 views
5

के साथ टाइप-सुरक्षित जेनेरिक कंटेनर मैक्रोज़ का उपयोग कर सी में एक प्रकार-सुरक्षित जेनेरिक लिंक्ड सूची बनाने की कोशिश कर रहा हूं। यह समान रूप से काम करना चाहिए कि टेम्पलेट्स सी ++ में कैसे काम करते हैं। उदाहरण के लिए,मैक्रोज़

LIST(int) *list = LIST_CREATE(int); 

मेरा पहला प्रयास #define LIST(TYPE) (मैक्रो मैं ऊपर प्रयोग किया जाता) एक struct _List_##TYPE {...} को परिभाषित करने के लिए किया गया था। हालांकि, यह काम नहीं किया क्योंकि हर बार जब मैंने एक नई सूची घोषित की तो संरचना को फिर से परिभाषित किया जाएगा। मैं ऐसा करने से समस्या निवारण:

/* You would first have to use this macro, which will define 
    the `struct _List_##TYPE`...        */ 
DEFINE_LIST(int); 

int main(void) 
{ 
    /* ... And this macro would just be an alias for the struct, it 
     wouldn't actually define it.         */ 
    LIST(int) *list = LIST_CREATE(int); 
    return 0; 
} 

/* This is how the macros look like */ 

#define DEFINE_LIST(TYPE) \ 
    struct _List_##TYPE  \ 
    {      \ 
     ...     \ 
    } 

#define LIST(TYPE)  \ 
    struct _List_##TYPE 

लेकिन एक और समस्या यह है कि जब मुझे लगता है कि DEFINE_LIST(int) उपयोग करते हैं, उदाहरण के लिए एक से अधिक फ़ाइलों है, और उनमें से कुछ एक दूसरे को शामिल करते हैं, तो अभी भी वही की कई परिभाषाएं हो जाएगा struct। क्या DEFINE_LIST बनाने का कोई तरीका है जांचें कि क्या संरचना पहले से ही परिभाषित की गई है?

/* one.h */ 
DEFINE_LIST(int); 

/* two.h */ 
#include "one.h" 
DEFINE_LIST(int); /* Error: already defined in one.h */ 
+0

वह वाक्यविन्यास अच्छा लग रहा है :) –

+0

[ओपनजीसी 3] (https://github.com/kevin-dong-nai-jia/OpenGC3) जो आप खोज रहे हैं। ['सीसीएक्सएल (टी)'] (https://github.com/kevin-dong-nai-jia/OpenGC3/blob/master/doc/ccxll-list.pdf) भी [नेस्टेड] हो सकता है (https: // gist.github.com/kevin-dong-nai-jia/af150182091f2871a92176b15965f814)! –

उत्तर

0

आप लाइब्रेरी का उपयोग क्यों नहीं करते? मैं GLib का उपयोग करना चाहते हैं, लेकिन मैं अपने कोड में शून्य संकेत नफरत है, आदेश GLib द्वारा उपलब्ध कराए गए आंकड़ों के प्रकार की एक typesafe संस्करण प्राप्त करने में मुझे कुछ बहुत ही सरल मैक्रो कोडित:

http://pastebin.com/Pc0KsadV

अगर मैं एक चाहते प्रतीक * की सूची मैं सिर्फ करने के लिए की जरूरत है (यह एक प्रकार मैंने पहले परिभाषित संभालने है):

GLIST_INSTANCE(SymbolList, Symbol*, symbol_list); 

आप एक सरल लिंक की एक पूरी पुस्तकालय (जो overkill का एक प्रकार हो सकता है) का उपयोग नहीं करना चाहते हैं सूची, एक सूची को कार्यान्वित करें जो शून्य * को संभालती है और सही प्रकार का कास्टिंग करने और बनाने के लिए कुछ फ़ंक्शन बनाती है।

+2

मुझे यकीन नहीं है कि टाइप-सुरक्षित के रूप में योग्यता प्राप्त होगी। –

+0

ये मैक्रोज़ मेरे लिए सुरक्षित प्रकार प्रतीत नहीं होते हैं। प्रत्येक अलग सूची के लिए जेनरेट किए गए फ़ंक्शंस किसी अन्य सूची प्रकार को स्वीकार करेंगे। – ugoren

+0

@ugoren, सच है, लेकिन वे एक अलग तर्क प्रकार स्वीकार नहीं करेंगे। उदाहरण के लिए, यदि आप 'symbol_list_append (अन्य टाइप सूची, अन्य टाइपऑब) कहते हैं, तो यह असफल हो जाएगा। – Victor

0

list_template.h फ़ाइल बनाने और फिर list_TYPE.h फ़ाइल बनाने और टेम्पलेट के प्रत्येक उदाहरण के लिए list_TYPE.c फ़ाइल बनाने के बारे में कैसे। ये निश्चित रूप से उचित हेडर रक्षक के साथ आ सकते हैं। आप केवल अपने टेम्पलेट हेडर को शामिल कर सकते हैं लेकिन संकलन और लिंक प्रक्रिया में सभी .c फ़ाइलों को जोड़ना सुनिश्चित करें, और इसे काम करना चाहिए।

यह वही सी ++ आप के लिए स्वचालित रूप से करता है ... उदाहरणों की नकल ...

1

तुम हमेशा एक दूसरा तर्क DEFINE_LIST मैक्रो है कि आप करने के लिए "नाम" सूची की अनुमति देगा करने के लिए जोड़ सकते हैं मूल रूप से है। उदाहरण के लिए:

#define DEFINE_LIST(TYPE, NAME)   \ 
struct _List_##TYPE_##NAME    \ 
{          \ 
    TYPE member_1;      \ 
    struct _List_##TYPE_##NAME* next; \ 
} 

तो फिर तुम बस कर सकता है:

DEFINE_LIST(int, my_list); 
//... more code that uses the "my_list" type 

तुम बस अपने आप को सीमित करने के लिए फिर से उपयोग नहीं कर एक ही सूची "नाम" जब दो अलग हेडर फाइल एक दूसरे को शामिल करना होगा, और दोनों DEFINE_LIST मैक्रो का उपयोग करें। LIST_CREATE, आदि का उपयोग करते समय आपको नाम से सूची का भी संदर्भ लेना होगा।

आपके द्वारा लिखे गए कार्यों में सूचियों को पार करते समय, आप हमेशा "जेनेरिक" प्रकार बना सकते हैं जो उपयोगकर्ता द्वारा परिभाषित "नामित" संस्करण को कास्ट किया जाता है इससे कुछ भी प्रभावित नहीं होना चाहिए क्योंकि struct में वास्तविक जानकारी वही रहती है, और "नाम" टैग केवल द्विआधारी दृष्टिकोण के बजाय घोषणाओं से भिन्नता को अलग करता है।उदाहरण के लिए, यहाँ एक समारोह है कि सूची की वस्तुओं कि int प्रकार की दुकान पर ले जाता है:

#define GENERIC_LIST_PTR(TYPE) struct _generic_list_type_##TYPE* 
#define LIST_CAST_PTR(OBJ, TYPE) (GENERIC_LIST_PTR(TYPE))(OBJ) 

void function(GENERIC_LIST_PTR(INT) list) 
{ 
    //...use list as normal (i.e., access it's int data-member, etc.) 
} 

DEFINE_LIST(int, my_list); 

int main() 
{ 
    LIST(int, my_list)* list = LIST_CREATE(int, my_list); 
    function(LIST_CAST_PTR(list, int)); 

    //...more code 

    return 0; 
} 

मैं जानता हूँ कि यह जरूरी सबसे सुविधाजनक बात नहीं है, लेकिन इस नामकरण के मुद्दों को हल करता है, और आप की क्या संस्करण नियंत्रित कर सकते हैं struct _generic_list_type_XXX कुछ निजी शीर्षलेख फ़ाइल में बनाए गए हैं जो अन्य उपयोगकर्ता इसमें शामिल नहीं होंगे (जब तक कि वे अपने स्वयं के प्रकारों के लिए ऐसा नहीं करना चाहते) ... लेकिन यह घोषणा और जेनेरिक सूची की परिभाषा को अलग करने के लिए एक तंत्र होगा वास्तविक उपयोगकर्ता परिभाषित सूची-प्रकार से टाइप करें।

+0

लेकिन फिर मैं एक निश्चित प्रकार की सूची स्वीकार करने वाले कार्यों को कैसे लिखूं? सूची के "नाम" को जानने के लिए उन्हें बहुत अच्छा लगता है। –

+0

आप "कास्ट" संस्करण महत्वपूर्ण हिस्सा नहीं होने के बाद से हमेशा एक कास्ट का उपयोग कर सकते हैं। मैं अपने जवाब में एक उदाहरण जोड़ूंगा। – Jason

0

मुझे सच में संदेह है कि आप एक मैक्रो में अस्तित्व की जांच कर सकते हैं और परिभाषित कर सकते हैं (एक संरचना)। #ifndefDEFINE_LIST(int) से पहले एक और रखो। यह सुरुचिपूर्ण नहीं है लेकिन आप जो चाहते हैं वह करता है।

8

मैंने सी ++ अधिग्रहित टेम्पलेट्स से पहले सी में इस समस्या का सामना किया और मैं अभी भी कोड है।

मैक्रोज़ के साथ आप वास्तव में जेनेरिक टाइपएफ़ कंटेनर-ऑफ-टी टेम्पलेट को परिभाषित नहीं कर सकते हैं जो पूरी तरह से हेडर फ़ाइलों तक ही सीमित है। मानक प्रीप्रोसेसर मैक्रो असाइनमेंट्स को "पुशिंग" और "पॉपिंग" करने का कोई माध्यम नहीं देता है, तो आप की आवश्यकता होगी ताकि विस्तारित और अनुक्रमिक विस्तार के संदर्भों के माध्यम से उनकी अखंडता को सुरक्षित रखा जा सके। और जैसे ही आप कंटेनर-ऑफ-कंटेनर-टी-टी को परिभाषित करके अपने कुत्ते के भोजन को खाने का प्रयास करेंगे, आपको नेस्टेड संदर्भों का सामना करना पड़ेगा।

बात है, किया जा सकता है के रूप में हम देखेंगे, लेकिन जैसा कि @immortal पता चलता है, यह जरूरत पर जोर देता है कि आप की आवश्यकता होती है टी के प्रत्येक मान के लिए अलग .h और .c फ़ाइलें पैदा होता है। आप, उदाहरण के लिए, एक पूरी तरह से सामान्य सूची के-टी मैक्रो के में एक इनलाइन फ़ाइल list_type.inl परिभाषित कर सकते हैं, कहते हैं,, और फिर एक में list_type.inl छोटे सेट-अप रैपर की जोड़ी में से प्रत्येक में शामिल हैं - list_float.h और list_float.c - कि क्रमशः सूची-के-फ्लोट कंटेनर को परिभाषित और कार्यान्वित करेगा। इसी प्रकार सूची-के-int, सूची-सूची-सूची के लिए, सूची-ऑफ-वेक्टर-ऑफ-लिस्ट-ऑफ-डबल, और इसी तरह के लिए।

एक योजनाबद्ध उदाहरण सभी स्पष्ट कर देगा। लेकिन सबसे पहले का पूरा उपाय प्राप्त करें-अपना-स्वयं-डॉगफूड चुनौती।

इस तरह के दूसरे ऑर्डर कंटेनर को सूची-सूची-सूची-सूची के रूप में मानें। हम हमारे मैक्रो सूची-के-टी समाधान के लिए T = list-of-thingummy सेट करके इन्हें तुरंत चालू करने में सक्षम होना चाहते हैं। लेकिन किसी भी तरह से सूचीबद्ध नहीं है एक पीओडी डेटाटाइप होने के लिए। चाहे सूची-का-सामान हमारी खुद की डॉगफूड है या किसी और का है, यह ढेर पर रहता है जो एक अमूर्त डेटाटाइप होने जा रहा है और को अपने उपयोगकर्ताओं को टाइपपीफ-एड पॉइंटर प्रकार के माध्यम से दर्शाया गया है। या कम से कम, यह ढेर पर रखे गतिशील घटकों के लिए जा रहा है। किसी भी मामले में, पीओडी नहीं।

इसका मतलब है कि यह हमारे सूची-के-टी समाधान के लिए पर्याप्त नहीं है बस यह बताया जाना चाहिए कि टी = सूची-की-चीज़। यह भी बताया जाना चाहिए कि क्या टी को गैर-पीओडी प्रति-निर्माण और विनाश की आवश्यकता है, और यदि ऐसा है तो एक की प्रतिलिपि बनाने और नष्ट करने के लिए कैसे। सी संदर्भ में, इसका मतलब है कि:

  • कॉपी-निर्माण: एक टी आकार अप्रतिबद्ध स्मृति के क्षेत्र में दिए गए टी की एक प्रतिलिपि बनाने के लिए, इस तरह के एक क्षेत्र के पते दिए गए।

  • विनाश: किसी दिए गए पते पर टी को कैसे नष्ट करें।

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

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

ध्यान देने के बाद, हम योजनाबद्ध उदाहरण के लिए सरल सूची-टी-समस्या के साथ रहेंगे; और अधिक सादगी के लिए मैं किसी भी const एपीआई की वांछनीयता भूल जाऊंगा।

इस और के लिए किसी अन्य टेम्पलेट कंटेनर प्रकार हम चाहते हैं कुछ टोकन-पेस्ट मैक्रो है कि हम आसानी से कार्य करता है और प्रकार, प्लस शायद अन्य उपयोगिता मैक्रो की पहचानकर्ता को इकट्ठा करते हैं। ये सभी एक शीर्षक में जाना है, कह सकते हैं macro_kit.h, जैसे:

#ifndef MACRO_KIT_H 
#define MACRO_KIT_H 

/* macro_kit.h */ 

#define _CAT2(x,y) x##y 

// Concatenate 2 tokens x and y 
#define CAT2(x,y) _CAT2(x,y) 
// Concatenate 3 tokens x, y and z 
#define CAT3(x,y,z) CAT2(x,CAT2(y,z)) 

// Join 2 tokens x and y with '_' = x_y 
#define JOIN2(x,y) CAT3(x,_,y) 
// Join 3 tokens x, y and z with '_' = x_y_z 
#define JOIN3(x,y,z) JOIN2(x,JOIN2(y,z)) 
// Compute the memory footprint of n T's 
#define SPAN(n,T) ((n) * sizeof(T)) 

#endif 

अब list_type.inl के योजनाबद्ध संरचना करने के लिए:

list_type.inl mutliple शामिल किए जाने के खिलाफ कोई मैक्रो-गार्ड है
//! There is intentionally no idempotence guard on this file 
#include "macro_kit.h" 
#include <stddef.h> 

#ifndef INCLUDE_LIST_TYPE_INL 
#error This file should only be included from headers \ 
that define INCLUDE_LIST_TYPE_INL 
#endif 

#ifndef LIST_ELEMENT_TYPE 
#error Need a definition for LIST_ELEMENT_TYPE 
#endif 

/* list_type.inl 

    Defines and implements a generic list-of-T container 
    for T the current values of the macros: 

    - LIST_ELEMENT_TYPE: 
     - must have a definition = the datatype (or typedef alias) for \ 
     which a list container is required. 

    - LIST_ELEMENT_COPY_INITOR: 
     - If undefined, then LIST_ELEMENT_TYPE is assumed to be copy- 
     initializable by the assignment operator. Otherwise must be defined 
     as the name of a copy initialization function having a prototype of 
     the form: 

     LIST_ELEMENT_TYPE * copy_initor_name(LIST_ELEMENT_TYPE *pdest, 
              LIST_ELEMENT_TYPE *psrc); 

     that will attempt to copy the LIST_ELEMENT_TYPE at `psrc` into the 
     uncommitted memory at `pdest`, returning `pdest` on success and NULL 
     on failure. 

     N.B. This file itself defines the copy initializor for the list-type 
     that it generates. 

    - LIST_ELEMENT_DISPOSE 
     If undefined, then LIST_ELEMENT_TYPE is assumed to need no 
     destruction. Otherwise the name of a destructor function having a 
     protoype of the form: 

     void dtor_name(LIST_ELEMENT_TYPE pt*); 

     that appropriately destroys the LIST_ELEMENT_TYPE at `pt`. 

     N.B. This file itself defines the destructor for the list-type that 
     it generates. 
*/ 

/* Define the names of the list-type to generate, 
    e.g. list_int, list_float 
*/ 
#define LIST_TYPE JOIN2(list,LIST_ELEMENT_TYPE) 

/* Define the function-names of the LIST_TYPE API. 
    Each of the API macros LIST_XXXX generates a function name in 
    which LIST becomes the value of LIST_TYPE and XXXX becomes lowercase, 
    e.g list_int_new 
*/ 
#define LIST_NEW JOIN2(LIST_TYPE,new) 
#define LIST_NODE JOIN2(LIST_TYPE,node) 
#define LIST_DISPOSE JOIN2(LIST_TYPE,dispose) 
#define LIST_COPY_INIT JOIN2(LIST_TYPE,copy_init) 
#define LIST_COPY JOIN2(LIST_TYPE,copy) 
#define LIST_BEGIN JOIN2(LIST_TYPE,begin) 
#define LIST_END JOIN2(LIST_TYPE,end) 
#define LIST_SIZE JOIN2(LIST_TYPE,size) 
#define LIST_INSERT_BEFORE JOIN3(LIST_TYPE,insert,before) 
#define LIST_DELETE_BEFORE JOIN3(LIST_TYPE,delete,before) 
#define LIST_PUSH_BACK JOIN3(LIST_TYPE,push,back) 
#define LIST_PUSH_FRONT JOIN3(LIST_TYPE,push,front) 
#define LIST_POP_BACK JOIN3(LIST_TYPE,pop,back) 
#define LIST_POP_FRONT JOIN3(LIST_TYPE,pop,front) 
#define LIST_NODE_GET JOIN2(LIST_NODE,get) 
#define LIST_NODE_NEXT JOIN2(LIST_NODE,next) 
#define LIST_NODE_PREV JOIN2(LIST_NODE,prev) 

/* Define the name of the structure used to implement a LIST_TYPE. 
    This structure is not exposed to user code. 
*/ 
#define LIST_STRUCT JOIN2(LIST_TYPE,struct) 

/* Define the name of the structure used to implement a node of a LIST_TYPE. 
    This structure is not exposed to user code. 
*/ 
#define LIST_NODE_STRUCT JOIN2(LIST_NODE,struct) 

/* The LIST_TYPE API... */ 


// Define the abstract list type 
typedef struct LIST_STRUCT * LIST_TYPE; 

// Define the abstract list node type 
typedef struct LIST_NODE_STRUCT * LIST_NODE; 

/* Return a pointer to the LIST_ELEMENT_TYPE in a LIST_NODE `node`, 
    or NULL if `node` is null 
*/ 
extern LIST_ELEMENT_TYPE * LIST_NODE_GET(LIST_NODE node); 

/* Return the LIST_NODE successor of a LIST_NODE `node`, 
    or NULL if `node` is null. 
*/ 
extern LIST_NODE LIST_NODE_NEXT(LIST_NODE node); 

/* Return the LIST_NODE predecessor of a LIST_NODE `node`, 
    or NULL if `node` is null. 
*/ 
extern LIST_NODE LIST_NODE_PREV(LIST_NODE node); 


/* Create a new LIST_TYPE optionally initialized with elements copied from 
    `start` and until `end`. 

    If `end` is null it is assumed == `start` + 1. 

    If `start` is not NULL then elements will be appended to the 
    LIST_TYPE until `end` or until an element cannot be successfully copied. 
    The size of the LIST_TYPE will be the number of successfully copied 
    elements. 
*/ 
extern LIST_TYPE LIST_NEW(LIST_ELEMENT_TYPE *start, LIST_ELEMENT_TYPE *end); 

/* Dispose of a LIST_TYPE 
    If the pointer to LIST_TYPE `plist` is not null and addresses 
    a non-null LIST_TYPE then the LIST_TYPE it addresses is 
    destroyed and set NULL. 
*/ 
extern void LIST_DISPOSE(LIST_TYPE * plist); 

/* Copy the LIST_TYPE at `psrc` into the LIST_TYPE-sized region at `pdest`, 
    returning `pdest` on success, else NULL. 

    If copying is unsuccessful the LIST_TYPE-sized region at `pdest is 
    unchanged. 
*/ 
extern LIST_TYPE * LIST_COPY_INIT(LIST_TYPE *pdest, LIST_TYPE *psrc); 

/* Return a copy of the LIST_TYPE `src`, or NULL if `src` cannot be 
    successfully copied. 
*/ 
extern LIST_TYPE LIST_COPY(LIST_TYPE src); 

/* Return a LIST_NODE referring to the start of the 
    LIST_TYPE `list`, or NULL if `list` is null. 
*/ 
extern LIST_NODE LIST_BEGIN(LIST_TYPE list); 

/* Return a LIST_NODE referring to the end of the 
    LIST_TYPE `list`, or NULL if `list` is null. 
*/ 
extern LIST_NODE LIST_END(LIST_TYPE list); 

/* Return the number of LIST_ELEMENT_TYPEs in the LIST_TYPE `list` 
    or 0 if `list` is null. 
*/ 
extern size_t LIST_SIZE(LIST_TYPE list); 

/* Etc. etc. - extern prototypes for all API functions. 
    ... 
    ... 
*/ 

/* If LIST_IMPLEMENT is defined then the implementation of LIST_TYPE is 
    compiled, otherwise skipped. #define LIST_IMPLEMENT to include this 
    file in the .c file that implements LIST_TYPE. Leave it undefined 
    to include this file in the .h file that defines the LIST_TYPE API. 
*/ 
#ifdef LIST_IMPLEMENT 
// Implementation code now included. 

// Standard library #includes...? 

// The heap structure of a list node 
struct LIST_NODE_STRUCT { 
    struct LIST_NODE_STRUCT * _next; 
    struct LIST_NODE_STRUCT * _prev; 
    LIST_ELEMENT_TYPE _data[1]; 
}; 

// The heap structure of a LIST_TYPE 
struct LIST_STRUCT { 
    size_t _size; 
    struct LIST_NODE_STRUCT * _anchor; 
}; 

/* Etc. etc. - implementations for all API functions 
    ... 
    ... 
*/ 

/* Undefine LIST_IMPLEMENT whenever it was defined. 
    Should never fall through. 
*/ 
#undef LIST_IMPLEMENT 

#endif // LIST_IMPLEMENT 

/* Always undefine all the LIST_TYPE parameters. 
    Should never fall through. 
*/ 
#undef LIST_ELEMENT_TYPE 
#undef LIST_ELEMENT_COPY_INITOR 
#undef LIST_ELEMENT_DISPOSE 
/* Also undefine the "I really meant to include this" flag. */ 

#undef INCLUDE_LIST_TYPE_INL 

ध्यान दें कि। आप कम से कम चाहते हैं - कम से कम टेम्पलेट एपीआई - प्रत्येक बार देखा जाना शामिल है।

यदि आप फ़ाइल के शीर्ष पर टिप्पणियां पढ़ते हैं तो आप अनुमान लगा सकते हैं कि आप कोड-ऑफ-इंटी कंटेनर प्रकार आयात करने के लिए एक रैपिंग हेडर कैसे कोड करेंगे।

#ifndef LIST_INT_H 
#define LIST_INT_H 

/* list_int.h*/ 

#define LIST_ELEMENT_TYPE int 
#define INCLUDE_LIST_TYPE_INL 
#include "list_type.inl" 

#endif 

और इसी तरह कैसे आप आयात करना रैपिंग हैडर कोड होगा एक सूची के- सूची के- पूर्णांक कंटेनर के प्रकार:

#ifndef LIST_LIST_INT_H 
#define LIST_LIST_INT_H 

/* list_list_int.h*/ 

#define LIST_ELEMENT_TYPE list_int 
#define LIST_ELEMENT_COPY_INIT list_int_copy_init 
#define LIST_ELEMENT_DISPOSE list_int_dispose 
#define INCLUDE_LIST_TYPE_INL 
#include "list_type.inl" 

#endif 

आपका आवेदन पत्र सुरक्षित रूप से इस तरह के रैपर शामिल कर सकते हैं, उदाहरण के लिए

#include "list_int.h" 
#include "list_list_int.h" 

इस तथ्य के बावजूद वे परस्पर विरोधी तरीकों से LIST_ELEMENT_TYPE परिभाषित क्योंकि list_type.inl हमेशा #undefs सभी मैक्रो उस सूची प्रकार parameterize जब यह उन लोगों के साथ किया जाता है: फ़ाइल के अंतिम कुछ लाइनों को देखने के।

नोट भी मैक्रो LIST_IMPLEMENT का उपयोग नोट करें। अगर यह list_type.inl को अनदेखा किया गया है तो केवल टेम्पलेट एपीआई का खुलासा किया गया है; टेम्पलेट कार्यान्वयन छोड़ दिया गया है। यदि LIST_IMPLEMENT परिभाषित किया गया है तो पूरी फ़ाइल संकलित की जाती है।इस प्रकार हमारे रैपिंग हेडर, LIST_IMPLEMENT को परिभाषित नहीं करके, केवल सूची-प्रकार API आयात करें।

हमारे रैपिंग स्रोत फ़ाइलों list_int.c, list_list_int.c के लिए इसके विपरीत

, हम LIST_IMPLEMENT परिभाषित करेगा। उसके बाद, वहाँ कोई लेना देना नहीं है, लेकिन इसी हेडर में शामिल हैं:

/* list_int.c */ 
#define LIST_IMPLEMENT 
#include "list_int.h" 

और:

/* list_list_int.c*/ 
#include "list_int.h" 
#define LIST_IMPLEMENT 
#include "list_list_int.h" 

अब अपने आवेदन में, कोई सूची-टेम्पलेट मैक्रो दिखाई देते हैं। (!): अपने लपेटकर हेडर "वास्तविक कोड" के लिए बाहर पार्स

#include "list_int.h" 
#include "list_list_int.h" 
// etc. 

int main(void) 
{ 
    int idata[10] = {1,2,3,4,5,6,7,8,9,10}; 
    //... 
    list_int lint = list_int_new(idata,idata + 10); 
    //... 
    list_list_int llint = list_list_int_new(&lint,0); 
    //... 
    list_int_dispose(&lint); 
    //... 
    list_list_int_dispose(&llint); 
    //... 
    exit(0); 
} 

एक 'सी टेम्पलेट लायब्रेरी "इस तरह से ही कड़ी मेहनत है प्रत्येक कंटेनर प्रकार के लिए .inl फ़ाइल में लिखने का साथ खुद को लैस करने के लिए आप चाहते हैं और इसे का परीक्षण करने के लिए बहुत अच्छी तरह से। इसके बाद आप शायद एक वस्तु फ़ाइल और ऑफ-द-शेल्फ लिंकेज के लिए देशी डेटाप्रकार और कंटेनर प्रकार के प्रत्येक संयोजन के हैडर उत्पन्न, और मांग पर अन्य प्रकार के लिए एक पल में .h और .c रैपर बाहर दस्तक होगा।

कहने की जरूरत नहीं है, जैसे ही सी ++ ने पर पसीने के लिए मेरे उत्साह को टेम्पलेट किया है, इस तरह उन्हें वाष्पित किया गया है। लेकिन यह इस तरह से किया जा सकता है, पूरी तरह से सामान्य रूप से, अगर किसी कारण से सी एकमात्र विकल्प है।

0

मैक्रोज़ के साथ सामान्य और प्रकार-सुरक्षित कंटेनर बनाना संभव है। गणना के सिद्धांत के दृष्टिकोण से, मैक्रो विस्तार से उत्पन्न भाषा (कोड) को नोडेटर्मेनिस्टिक पुशडाउन ऑटोमाटा द्वारा पहचाना जा सकता है जिसका अर्थ है कि यह सबसे अधिक संदर्भ-मुक्त व्याकरण है। उपर्युक्त बयान से हमारा लक्ष्य प्राप्त करना असंभव प्रतीत होता है क्योंकि कंटेनर और उसके संबद्ध इटरेटर्स को उनके प्रकार के प्रकार को याद रखना चाहिए, लेकिन यह केवल संदर्भ-संवेदनशील व्याकरण द्वारा किया जा सकता है। हालांकि, हम कुछ चाल कर सकते हैं!

सफलता की कुंजी संकलन प्रक्रिया में निहित है, प्रतीक तालिकाओं का निर्माण। यदि चर का प्रकार पहचाना जा सकता है जब कंपाइलर तालिका से पूछताछ करता है और कोई असुरक्षित प्रकार कास्टिंग नहीं होता है, तो इसे टाइप-सुरक्षित माना जाता है। इसलिए, हमें प्रत्येक struct को एक विशेष नाम देना होगा क्योंकि यदि दो या दो से अधिक structs समान स्तर पर घोषित किए जाते हैं तो संरचना का नाम संघर्ष कर सकता है। सबसे आसान तरीका वर्तमान पंक्ति संख्या को संरचना नाम में जोड़ना है। मानक सी एएनएसआई सी (सी 8 9/सी 9 0) के बाद पूर्वनिर्धारित मैक्रो __LINE__ और मैक्रो concatenation/stringification का समर्थन करता है।

फिर, हमें क्या करना है, जो हमने ऊपर परिभाषित संरचना में कुछ विशेषताओं को छिपाना है। यदि आप रन-टाइम पर एक और सूची रिकॉर्ड बनाना चाहते हैं, तो संरचना में स्वयं को एक पॉइंटर डालें, वास्तव में समस्या को हल करेगा। हालांकि, यह पर्याप्त नहीं है। हमें रन-टाइम पर आवंटित कितने सूची रिकॉर्ड स्टोर करने के लिए एक अतिरिक्त चर की आवश्यकता हो सकती है। यह प्रोग्रामर द्वारा स्पष्ट रूप से नष्ट होने पर स्मृति को मुक्त करने का तरीका जानने में हमारी सहायता करता है। इसके अलावा, हम __typeof__() एक्सटेंशन का लाभ ले सकते हैं जिसका व्यापक रूप से मैक्रो प्रोग्रामिंग में उपयोग किया जाता है।

ccxll(int) list;      // declare a list of type int 
ccxll_init(list);      // initialize the list record 

for (int cnt = 8; cnt-- > 0;)  // 
    ccxll_push_back(list, rand()); // insert "rand()" to the end 

ccxll_sort(list);      // sort with comparator: XLEQ 

CCXLL_INCR_AUTO(pnum, list)   // traverse the list forward: 
    printf("num = %d\n", *pnum);  // access elems through iters 

ccxll_free(list);      // destroy the list after use 

यह काफी के समान है:

मैं OpenGC3 जो मैक्रो के साथ इमारत प्रकार सुरक्षित सामान्य कंटेनरों करना है, और यहाँ है कि यह कैसे काम करता है पुस्तकालय की एक छोटी और संक्षिप्त उदाहरण के लेखक हूँ एसटीएल का वाक्यविन्यास। सूची का प्रकार निर्धारित किया जाता है जब list घोषित किया जाता है। हमें प्रकार की सुरक्षा के बारे में चिंता करने की आवश्यकता नहीं है क्योंकि सूची में संचालन किए जाने पर कोई असुरक्षित प्रकार कास्टिंग नहीं होता है।

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