2012-05-10 21 views
19

कई बार मैंने एक समारोह तर्क के परिवर्तनशील, शून्य द्वारा समाप्त प्राप्त करना चाहते हैं, उदाहरण के लिएtypesafe जीसीसी के साथ सी में varargs

#define push(stack_t stack, ...) _push(__VARARG__, NULL); 
func _push(stack_t stack, char *s, ...) { 
    va_list args; 
    va_start(args, s); 
    while (s = va_arg(args, char*)) push_single(stack, s); 
} 

मैं जीसीसी या बजना निर्देश दे सकते हैं चेतावनी देने के लिए करता है, तो foo गैर char* चर प्राप्त करता है ? __attribute__(format) के समान कुछ, लेकिन एक ही सूचक प्रकार के कई तर्कों के लिए।

+2

यदि सभी प्रकारों को एक ही प्रकार की आवश्यकता है, तो क्या आपने उन्हें केवल एक सरणी में गुजरने पर विचार किया है? –

+0

C89 में कोई मूल सरणी नहीं है। आप एमएस सी कंपाइलर के साथ, 'एफ ({1,2,3,0}) पास नहीं कर सकते हैं। – mikebloch

+3

आप कुछ ऐसा चाहते हैं जो जीसीसी या एमएस सी के साथ काम करता हो? कृपया उचित टैग करें। सी 99 के साथ ऐसे समाधान हैं जो टाइपएफ़ हैं। –

उत्तर

15

मुझे पता है तुम __attribute__((sentinel)) किसी भी तरह का उपयोग करने का सोच रहे हैं, लेकिन यह एक रेड हेरिंग है।

क्या आप चाहते हैं कुछ इस तरह करना है:

#define push(s, args...) ({     \ 
    char *_args[] = {args};      \ 
    _push(s,_args,sizeof(_args)/sizeof(char*)); \ 
}) 

जो लपेटता:

void _push(stack_t s, char *args[], int argn); 

जो आप वास्तव में लिख सकते हैं जिस तरह से आप आशा करता हूं कि आप इसे लिख सकते हैं!

तो फिर तुम फोन कर सकते हैं:

push(stack, "foo", "bar", "baz"); 
push(stack, "quux"); 
+0

क्या यह 'char *' पॉइंटर्स की अनावश्यक प्रतिलिपि नहीं करेगा? –

+0

@ ची-लैन - '_push()' जीसीसी के कार्यान्वयन के आधार पर अनावश्यक भार हटा दिए जाएंगे। – geocar

+0

मैं इस तरह कुछ करने की कोशिश कर रहा हूं। मैं वास्तव में एक __attribute चाहता हूं जो varargs की प्रकार की सुरक्षा की गारंटी देता है। असल में मैं यह सुनिश्चित करना चाहता हूं कि वे सभी char * हैं। कोई जानता है कि उसे कैसे करना है? –

1

मैं केवल कुछ इस तरह के बारे में सोच सकते हैं:

#include <stddef.h> 
#include <stdio.h> 
#include <stdlib.h> 

typedef struct tArg 
{ 
    const char* Str; 
    struct tArg* Next; 
} tArg; 

tArg* Arg(const char* str, tArg* nextArg) 
{ 
    tArg* p = malloc(sizeof(tArg)); 
    if (p != NULL) 
    { 
    p->Str = str; 
    p->Next = nextArg; 
    } 
    else 
    { 
    while (nextArg != NULL) 
    { 
     p = nextArg->Next; 
     free(nextArg); 
     nextArg = p; 
    } 
    } 
    return p; 
} 

void PrintR(tArg* arg) 
{ 
    while (arg != NULL) 
    { 
    tArg* p; 
    printf("%s", arg->Str); 
    p = arg->Next; 
    free(arg); 
    arg = p; 
    } 
} 

void (*(*(*(*(*(*(*Print8 
    (const char* Str)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*) 
{ 
    printf("%s", Str); 
    // There's probably a UB here: 
    return (void(*(*(*(*(*(*(*) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*)) 
    (const char*))&Print8; 
} 

int main(void) 
{ 
    PrintR(Arg("HELLO", Arg(" ", Arg("WORLD", Arg("!", Arg("\n", NULL)))))); 
// PrintR(Arg(1, NULL));  // warning/error 
// PrintR(Arg(&main, NULL)); // warning/error 
// PrintR(Arg(0, NULL));  // no warning/error 
// PrintR(Arg((void*)1, NULL)); // no warning/error 

    Print8("hello")(" ")("world")("!")("\n"); 
// Same warning/error compilation behavior as with PrintR() 
    return 0; 
} 
+3

ओह भाई! मुझे यकीन नहीं है कि हंसी या रोना है ...;-) –

+0

@ ची-लैन: सी असाधारण रूप से अभिव्यक्तिपूर्ण है। :) –

+0

'प्रिंट 8' मैंने कभी देखा है सी का सबसे अच्छा और सबसे खराब बिट है। – huon

-1

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

यह सारी जानकारी वास्तव में भाषा में उचित वाक्यविन्यास में एन्कोड किया जाना चाहिए।

उदाहरण के लिए, एक औपचारिक मानकों के साथ मौजूदा सी वाक्य रचना अंडाकार के बाद की तरह तो

void foo (... int counter, float arglist); 

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

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

void foo (... int counter, float arglist) { 
    unsigned i; 
    for (i=0; i<counter; i++) { 
    printf("list[%i] = %f\n", i, arglist[i]); 
    } 
} 

की तरह ही भाषा में बनी इस तरह के एक सुविधा के साथ , arglist[i] के प्रत्येक संदर्भ को तब स्टैक फ्रेम पर संबंधित पते पर अनुवादित किया जाएगा। मैक्रोज़ के माध्यम से ऐसा करने की आवश्यकता नहीं होगी।

इसके अलावा, तर्क गणना स्वचालित रूप से संकलक द्वारा डाली जाएगी, और त्रुटि के लिए अवसर कम कर देगा।

foo(1.23, 4.56, 7.89); 

के लिए एक फोन के रूप में अगर यह

foo(3, 1.23, 4.56, 7.89); 

लिखा गया था समारोह शरीर के भीतर बहस वास्तव में पारित कर दिया हो सकता है की वास्तविक संख्या से परे एक तत्व के लिए किसी भी पहुँच संकलित किया जाएगा, रनटाइम पर चेक किया गया और एक संकलन समय गलती का कारण बनता है, जिससे सुरक्षा में काफी वृद्धि होती है।

अंतिम लेकिन कम से कम नहीं, सभी वैरिएडिक पैरामीटर टाइप किए गए हैं और संकलित समय पर टाइप किए जा सकते हैं जैसे गैर-वैरिएडिक पैरामीटर चेक किए जाते हैं।

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

void store (collection dict, ... int counter, key_t key, val_t value); 

इस समारोह तो

store(dict, key1, val1, key2, val2, key3, val3); 

लेकिन जैसा कि कहा जा सकता है की तरह संकलित किया जाएगा जैसे कि वह

लिखा गया था
store(dict, 3, key1, val1, key2, val2, key3, val3); 

वास्तविक पैरामीटर के प्रकार इसी भिन्नता औपचारिक पैरामीटर के विरुद्ध समय संकलित किए जाएंगे।

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

key[i] i-वें मुख्य/मान जोड़े के प्रमुख को संदर्भित करता है value[i] i-th मान जोड़ी

के मान को संदर्भित करता है और इन संदर्भों को स्टैक फ्रेम पर उनके संबंधित पते पर संकलित किया जाएगा।

इनमें से कोई भी वास्तव में करना मुश्किल नहीं है, और न ही यह कभी भी रहा है। हालांकि, सी के डिजाइन दर्शन बस ऐसी सुविधाओं के लिए अनुकूल नहीं है।

एक venturing सी संकलक implementor (या सी पूर्वप्रक्रमक implementor) आगे निकल इस लागू करने के लिए या एक इसी तरह की योजना इसके बिना संभावना नहीं है हम कभी भी सी में इस तरह का कुछ भी देखेंगे

दिक्कत यह है कि है लोग सुरक्षा में रुचि रखते हैं और अपने स्वयं के कंपाइलर्स बनाने के लिए काम में रखने के इच्छुक हैं, आमतौर पर इस निष्कर्ष पर आते हैं कि सी भाषा बचाई से परे है और साथ ही साथ शुरू करने के लिए एक बेहतर डिजाइन भाषा के साथ शुरू हो सकता है।

मैं वहां स्वयं रहा हूं, अंततः प्रयास को त्यागने का फैसला किया, फिर विर्थ की भाषाओं में से एक को कार्यान्वित किया और इसके बजाय उस प्रकार के सुरक्षित वैरिएडिक्स को जोड़ा। तब से मैंने उन लोगों में भाग लिया है जिन्होंने मुझे अपने निरस्त प्रयासों के बारे में बताया था। सी में उचित प्रकार सुरक्षित भिन्नता प्रतीत होता है कि वह छिपी रहती है।

+0

कारण वाक्यविन्यास के साथ क्यों नहीं करना है, लेकिन एबीआई में परिवर्तन: x86_64 एबीआई और i386 फास्टकॉल एबीआई उदाहरण के लिए उन तर्कों की आवश्यकता नहीं है, जिनके पास "सीपीयू" है, और वास्तव में आधुनिक सीपीयू पर स्टैक इंजन तर्कों को फैलाने की लागत के लिए बहुत कुछ करें, बहुमूल्य एल 1 की डबल-अपशिष्ट इसे नेट-लॉस बनाता है। यदि आप सिंटैक्स और अभिव्यक्ति के साथ खेलना चाहते थे, तो आप सी 4 का प्रयास कर सकते हैं क्योंकि इसका अपना (वर्चुअल) एबीआई है वैसे भी: https://github.com/rswier/c4 – geocar

+0

यदि यह वाक्यविन्यास भाषा में डिज़ाइन किया गया था, तो कार्यान्वयनकर्ताओं को इसे अपने संबंधित एबीआई को मैप करना होगा। ऐसा कोई कारण नहीं है कि आप ऐसा क्यों नहीं कर सके। संदर्भ संकलन समय (कम से कम स्थानापन्न संदर्भों के संदर्भ में) पर हल किए जाते हैं और संकलक को लक्षित एबीआई का ज्ञान होता है। इसके अलावा, कोई कारण नहीं है कि एक रजिस्टर सेट में एक भिन्न तर्क सूची को मैप नहीं किया जा सका। कार्यान्वयन प्रयास वीए मैक्रोज़ को लागू करने के समान ही है। असली मुद्दा यह है कि परिवर्तनीय पैरामीटर अज्ञात हैं। यह इसे सुरक्षित बनाने के तरीके में खड़ा है। – trijezdci

+0

सी को एबीआई के चारों ओर डिजाइन किया गया था, न कि दूसरी तरफ। यदि आप सी की तरह दिखने वाले पास्कल चाहते हैं, तो आप सी 4 में से एक का निर्माण कर सकते हैं जैसा कि मेरा सुझाव है - यह आर्किटेक्चररी रूप से कई पास्कल कार्यान्वयन के समान है। – geocar

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