2014-08-27 8 views
7

के बिना पूर्ण encapsulation मैं केवल एक अपूर्ण घोषणा के साथ ढेर पर एक संरचना चर घोषित करने की कोशिश कर, सी 11 और वीएलए के साथ प्रयोग कर रहा था। उद्देश्य आंतरिक संरचना (जैसे पीआईएमपीएल मुहावरे) के बिना कुछ संरचना प्रकार के चर को बनाने के लिए एक तंत्र प्रदान करना है, लेकिन ढेर पर चर बनाने की आवश्यकता के बिना और उस पर एक सूचक वापस लौटना। साथ ही, यदि संरचना लेआउट बदलता है, तो मैं संरचना का उपयोग करने वाली प्रत्येक फ़ाइल को पुन: संकलित नहीं करना चाहता हूं।मॉलोक

private.h:

#ifndef PRIVATE_H_ 
#define PRIVATE_H_ 

typedef struct A{ 
    int value; 
}A; 

#endif /* PRIVATE_H_ */ 

public.h:

#ifndef PUBLIC_H_ 
#define PUBLIC_H_ 

typedef struct A A; 

size_t A_getSizeOf(void); 

void A_setValue(A * a, int value); 

void A_printValue(A * a); 

#endif /* PUBLIC_H_ */ 

implementation.c:

#include "private.h" 
#include "stdio.h" 

size_t A_getSizeOf(void) 
{ 
    return sizeof(A); 
} 

void A_setValue(A * a, int value) 
{ 
    a->value = value; 
} 

void A_printValue(A * a) 
{ 
    printf("%d\n", a->value); 
} 

मुख्य

मैं कार्यक्रम के लिए निम्न में कामयाब रहे । सी:

#include <stdalign.h> 
#include <stddef.h> 

#include "public.h" 

#define createOnStack(type, variable) \ 
    alignas(max_align_t) char variable ## _stack[type ## _getSizeOf()]; \ 
    type * variable = (type *)&variable ## _stack 

int main(int argc, char *argv[]) { 
    createOnStack(A, var); 

    A_setValue(var, 5335); 
    A_printValue(var); 
} 

मैंने इस कोड का परीक्षण किया है और ऐसा लगता है। हालांकि मुझे यकीन नहीं है कि क्या मैं कुछ दिख रहा हूं (जैसे एलियासिंग, संरेखण या ऐसा कुछ) जो खतरनाक या अयोग्य हो सकता है, या प्रदर्शन को नुकसान पहुंचा सकता है। इसके अलावा मैं जानना चाहता हूं कि सी

+0

आप समझदारी से जब struct लेआउट परिवर्तन sizeof एक संकलन समय के लिए अनुकूलित किया जाएगा के रूप में recompiling के बिना ऐसा नहीं कर सकते लगातार यदि आप एक VLA या alloca – Vality

+2

@Vality का उपयोग करें: कोड में फिर से देखो - कि एक कड़ी समय अनुकूलन होगा ; मैबस तर्क तर्क – Christoph

+0

@ मैबस इसे पोस्ट करने के लिए धन्यवाद होना चाहिए। मैंने अन्य प्रकार के भंडारण प्रदान करने के लिए वर्णों के वीएलए का उपयोग करने के बारे में सोचा नहीं था। –

उत्तर

4

यह निश्चित रूप से प्रभावी टाइपिंग नियमों (उर्फ सख्त एलियासिंग) का उल्लंघन करता है क्योंकि सी भाषा टाई char [] के किसी ऑब्जेक्ट को अनुमति नहीं देती है एक सूचक के माध्यम से पहुंचाया गया है जिसमें उस प्रकार (या एक संगत एक) नहीं है।

आप -fno-strict-aliasing तरह संकलक झंडे के माध्यम से अक्षम कर सकते हैं सख्त अलियासिंग विश्लेषण या

#ifdef __GNUC__ 
#define MAY_ALIAS __attribute__((__may_alias__)) 
#else 
#define MAY_ALIAS 
#endif 

तरह जिम्मेदार बताते हैं (धन्यवाद .. आर के लिए जाने के बाद उनका कहना है के लिए), लेकिन आप ऐसा नहीं करते हैं, भले ही इसलिए, अभ्यास में सब कुछ तब तक ठीक काम करेगा जब तक आप टाइप किए गए पॉइंटर को प्रारंभ करने के लिए केवल चर के उचित नाम का उपयोग न करें।

व्यक्तिगत रूप से, मैं

#define stackbuffer(NAME, SIZE) \ 
    _Alignas (max_align_t) char NAME[SIZE] 

typedef struct Foo Foo; 
extern const size_t SIZEOF_FOO; 

stackbuffer(buffer, SIZEOF_FOO); 
Foo *foo = (void *)buffer; 

विकल्प की तर्ज पर कुछ करने के लिए अपने घोषणाओं को आसान बनाने में अमानक alloca() का उपयोग कर किया जाएगा चाहते हैं, लेकिन है कि 'समारोह' मुद्दों के अपने स्वयं के सेट के साथ आता है।

+1

यदि आप '-फनो-सख्त-एलियासिंग' के साथ एक जीएनयू-सी-संगत कंपाइलर ग्रहण करने के इच्छुक हैं, तो उस ध्वज के साथ पूरे कार्यक्रम के अनुकूलन को बर्बाद करने के बजाय, आपको '__attribute __ ((__ may_alias __)) ' प्रकार 'फू'। यह केवल एक प्रकार के परिणाम प्राप्त करना चाहिए। –

+0

यह सख्त एलियासिंग का उल्लंघन क्यों करता है? मैंने सोचा कि चारों ओर एक पॉइंटर समस्या के बिना किसी भी अन्य सूचक के साथ उपनाम कर सकते हैं। – Mabus

+0

@ मैबस: चार के लिए एक पॉइंटर कुछ भी हो सकता है, लेकिन यह विपरीत मामला है - एक चरित्र सरणी को 'Foo' aliasing करने के लिए एक सूचक; प्रभावी प्रकारों के संदर्भ में सोचना बेहतर होता है: मेमोरी ब्लॉक 'बफर' में प्रभावी प्रकार 'char [] 'है, लेकिन आप इसे' Foo' – Christoph

1

मैं अनिवार्य रूप से एक ही समस्या को हल करने के लिए निम्न की तरह एक रणनीति को अपनाने पर विचार कर रहा हूं। शायद एक साल देर होने के बावजूद यह ब्याज का होगा।

मैं अपने राज्य के बारे में तर्क और विश्वसनीय डिजाइन अनुबंध लिखना आसान बनाने के लिए सीधे संरचनाओं के ग्राहकों को रोकने की इच्छा करता हूं। मैं ढेर पर छोटी संरचनाओं को आवंटित करने से भी बचाना पसंद करूंगा। लेकिन मैं एक सी 11 सार्वजनिक इंटरफ़ेस बर्दाश्त नहीं कर सकता - सी का अधिकांश आनंद यह है कि लगभग कोई भी कोड जानता है कि सी 8 9 से बात कैसे करें।

इसके लिए पर्याप्त आवेदन कोड पर विचार करें:

#include "opaque.h" 
int main(void) 
{ 
    opaque on_the_stack = create_opaque(42,3.14); // constructor 
    print_opaque(&on_the_stack); 
    delete_opaque(&on_the_stack); // destructor 
    return 0; 
} 

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

/* opaque.h */ 
#ifndef OPAQUE_H 
#define OPAQUE_H 

/* max_align_t is not reliably available in stddef, esp. in c89 */ 
typedef union 
{ 
    int foo; 
    long long _longlong; 
    unsigned long long _ulonglong; 
    double _double; 
    void * _voidptr; 
    void (*_voidfuncptr)(void); 
    /* I believe the above types are sufficient */ 
} alignment_hack; 

#define sizeof_opaque 16 /* Tedious to keep up to date */ 
typedef struct 
{ 
    union 
    { 
    char state [sizeof_opaque]; 
    alignment_hack hack; 
    } private; 
} opaque; 
#undef sizeof_opaque /* minimise the scope of the macro */ 

void print_opaque(opaque * o); 
opaque create_opaque(int foo, double bar); 
void delete_opaque(opaque *); 
#endif 

अंत में एक कार्यान्वयन, जिसका स्वागत सी 11 का उपयोग करने के लिए किया जाता है क्योंकि यह इंटरफ़ेस नहीं है। _Static_assert (alignof ...) विशेष रूप से आश्वस्त है। रैप/अनैप परतों को उत्पन्न करने के स्पष्ट परिशोधन को इंगित करने के लिए स्थिर कार्यों की कई परतों का उपयोग किया जाता है। बहुत सारी गड़बड़ कोड जेन के लिए उपयुक्त है।

#include "opaque.h" 

#include <stdalign.h> 
#include <stdio.h> 

typedef struct 
{ 
    int foo; 
    double bar; 
} opaque_impl; 

/* Zero tolerance approach to letting the sizes drift */ 
_Static_assert(sizeof (opaque) == sizeof (opaque_impl), "Opaque size incorrect"); 
_Static_assert(alignof (opaque) == alignof (opaque_impl), "Opaque alignment incorrect"); 

static void print_opaque_impl(opaque_impl *o) 
{ 
    printf("Foo = %d and Bar = %g\n",o->foo,o->bar); 
} 

static void create_opaque_impl(opaque_impl * o, int foo, double bar) 
{ 
    o->foo = foo; 
    o->bar = bar; 
} 

static void create_opaque_hack(opaque * o, int foo, double bar) 
{ 
    opaque_impl * ptr = (opaque_impl*)o; 
    create_opaque_impl(ptr,foo,bar); 
} 

static void delete_opaque_impl(opaque_impl *o) 
{ 
    o->foo = 0; 
    o->bar = 0; 
} 

static void delete_opaque_hack(opaque * o) 
{ 
    opaque_impl * ptr = (opaque_impl*)o; 
    delete_opaque_impl(ptr); 
} 

void print_opaque(opaque * o) 
{ 
    return print_opaque_impl((opaque_impl*)o); 
} 

opaque create_opaque(int foo, double bar) 
{ 
    opaque tmp; 
    unsigned int i; 
    /* Useful to zero out padding */ 
    for (i=0; i < sizeof (opaque_impl); i++) 
    { 
     tmp.private.state[i] = 0; 
    } 
    create_opaque_hack(&tmp,foo,bar); 
    return tmp; 
} 

void delete_opaque(opaque *o) 
{ 
    delete_opaque_hack(o); 
} 

कमियां मैं अपने आप को देख सकते हैं:

  1. आकार बदलने मैन्युअल परेशान किया जाएगा
  2. कास्टिंग में बाधा चाहिए अनुकूलन को परिभाषित (मैं अभी तक इस जांच न की हो)
  3. यह हो सकता है सख्त सूचक एलियासिंग का उल्लंघन करें। कल्पना को फिर से पढ़ने की जरूरत है।

मैं गलती से अपरिभाषित व्यवहार का आह्वान करने के बारे में चिंतित हूं। मुझे उपरोक्त पर सामान्य प्रतिक्रिया में दिलचस्पी होगी, या यह प्रश्न में आविष्कारक वीएलए तकनीक के लिए एक विश्वसनीय विकल्प की तरह दिखता है।

+0

सबसे स्पष्ट समस्या जो मैं देखता हूं वह यह है कि यदि आप sizeof_opaque बदलते हैं तो आपके पास बाइनरी संगतता नहीं हो सकती है। वीएलए कार्यान्वयन के साथ, आप संरचना का आकार बदल सकते हैं और संकलित प्रोग्राम अभी भी काम कर रहे हैं (मान लीजिए कि आप साझा लाइब्रेरी प्रोग्रामिंग कर रहे हैं)। यह भी ध्यान रखें कि यदि आप एक्सटेंशन के साथ सी 99 कोड लिखना पसंद करते हैं तो जीसीसी और अन्य कंप्यूटर्स के पास एलाइनिज़ (गठबंधन) के समान गुण होता है। साथ ही, जब आप अपनी लाइब्रेरी के बाहर किसी अन्य के अंदर एक संरचना बना रहे हैं, तो आपको इसे मेरे मामले में मॉलोक करना होगा, क्योंकि आप संकलन समय पर आकार नहीं जानते हैं। – Mabus

+0

@ मैबस यह सच है, आकार बदलना बाइनरी संगतता को तोड़ देगा। वीएलए या मॉलोक शायद एकमात्र कामकाज हैं। उज्ज्वल तरफ, अपारदर्शी संरचनाओं की संरचना ढेर के बिना आकार तय होने पर अच्छी तरह से काम करती है। हमेशा ट्रेडऑफ! पुस्तकालयों की बाइनरी संगतता कुछ है जो मुझे (ज्यादा) देने के लिए आवश्यक है। –

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