किसी अन्य फ़ंक्शन के लिए चर तर्कों को पास करना जो एक चर तर्क सूची को स्वीकार करता है


114

इसलिए मेरे पास 2 कार्य हैं जो दोनों में समान तर्क हैं

void example(int a, int b, ...);
void exampleB(int b, ...);

अब exampleकॉल करता है exampleB, लेकिन मैं परिवर्तन के बिना चर तर्क सूची में चर के साथ कैसे गुजर सकता हूं exampleB(जैसा कि यह पहले से ही कहीं और भी उपयोग किया जाता है)।



2
खैर उस एक पर समाधान vprintf का उपयोग कर रहा था, और यहाँ ऐसा नहीं है।
उपलब्ध नहीं है

यह संबंधित है, लेकिन निश्चित रूप से समान नहीं है, प्रस्तावित डुप्लिकेट: सी में एक चर समारोह के एक आह्वान को अग्रेषित करें?
जोनाथन लेफ्लर 23

जवाबों:


127

आप इसे सीधे नहीं कर सकते हैं; आपको एक ऐसा फंक्शन बनाना होगा जो एक va_list:

#include <stdarg.h>

static void exampleV(int b, va_list args);

void exampleA(int a, int b, ...)    // Renamed for consistency
{
    va_list args;
    do_something(a);                // Use argument a somehow
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

void exampleB(int b, ...)
{
    va_list args;
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

static void exampleV(int b, va_list args)
{
    ...whatever you planned to have exampleB do...
    ...except it calls neither va_start nor va_end...
}

2
मुझे संदेह है कि मुझे ऐसा कुछ करना पड़ा, समस्या उदाहरण समारोह है मूल रूप से vsprintf के लिए एक रैपर है और बहुत कुछ नहीं: /
उपलब्ध

@Xeross: ध्यान दें कि यह बाहरी विनिर्देश को नहीं बदलता है जो exampleB करता है - यह सिर्फ आंतरिक कार्यान्वयन को बदलता है। मुझे यकीन नहीं है कि समस्या क्या है।
जोनाथन लेफ्लर 4

क्या पहले पैरामीटर की आवश्यकता है या सभी पैरामीटर वैरिएडिक हो सकते हैं?
क्वर्टी

1
@Qwerty: सिंटैक्स को फ़ंक्शन में एक नामित प्रथम तर्क की आवश्यकता होती है , ...जो हस्ताक्षर में एक चर तर्क सूची लेता है । यदि आपने चर तर्कों को a में बदल दिया है va_list, तो आप va_listकिसी अन्य फ़ंक्शन को पारित कर सकते हैं जो केवल एक लेता है va_list, लेकिन उस फ़ंक्शन (या जिसे वह कॉल करता है) को यह जानने का कुछ तरीका होना चाहिए कि क्या है va_list
जोनाथन लेफ्लर

46

हो सकता है कि यहां एक तालाब में एक चट्टान को फेंक दिया जाए, लेकिन यह C ++ 11 वैरेडिक टेम्प्लेट के साथ बहुत अच्छा काम करता है:

#include <stdio.h>

template<typename... Args> void test(const char * f, Args... args) {
  printf(f, args...);
}

int main()
{
  int a = 2;
  test("%s\n", "test");
  test("%s %d %d %p\n", "second test", 2, a, &a);
}

बहुत कम से कम, यह साथ काम करता है g++


मैं उलझन में हूँ - क्या यह C ++> = 11 का उपयोग करके एक वैध तरीका है?
डंकन जोन्स

@DuncanJones हाँ, पैक का विस्तार होता हैargs...
स्वोर्डफ़िश

15

आपको इन कार्यों के संस्करण बनाने चाहिए जो va_list लेते हैं, और उन्हें पास करते हैं। को देखो vprintfएक उदाहरण के रूप:

int vprintf ( const char * format, va_list arg );

5

मैं भी प्रिंटफ़ लपेटना चाहता था और यहाँ एक उपयोगी उत्तर मिला:

कैसे प्रिंट / स्प्रिंट करने के लिए तर्कों की चर संख्या पास करें

मुझे प्रदर्शन में कोई दिलचस्पी नहीं थी (मुझे यकीन है कि कोड के इस टुकड़े को कई तरीकों से बेहतर बनाया जा सकता है, ऐसा करने के लिए स्वतंत्र महसूस करें :)), यह सामान्य डिबगप्रिन्टिंग के लिए है, इसलिए मैंने ऐसा किया:

//Helper function
std::string osprintf(const char *fmt, ...)
{
    va_list args;
    char buf[1000];
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args );
    va_end(args);
    return buf;
}

जो मैं फिर इस तरह का उपयोग कर सकते हैं

Point2d p;

cout << osprintf("Point2d: (%3i, %3i)", p.x, p.y);
instead of for example:
cout << "Point2d: ( " << setw(3) << p.x << ", " << p.y << " )";

सी ++ ऑस्ट्रेसेस कुछ पहलुओं में सुंदर हैं, लेकिन व्यावहारिक रूप से भयावह हो जाते हैं यदि आप कुछ छोटे स्ट्रिंग्स जैसे कि कोष्ठक, कॉलन और कॉमा के साथ सम्मिलित संख्याओं के बीच कुछ प्रिंट करना चाहते हैं।


2

संयोग से, कई सी कार्यान्वयन में आंतरिक v? Printf भिन्नता है जो IMHO को C मानक का हिस्सा होना चाहिए था। सटीक विवरण अलग-अलग होते हैं, लेकिन एक विशिष्ट कार्यान्वयन एक संरचना को स्वीकार करेगा जिसमें एक चरित्र-आउटपुट फ़ंक्शन पॉइंटर और जानकारी होती है जो कहती है कि क्या होने वाला है। इससे प्रिंटफ, स्प्रिंटफ और फ़र्फ़रफ़ सभी एक ही 'कोर' तंत्र का उपयोग कर सकते हैं। उदाहरण के लिए, vsprintf कुछ इस तरह हो सकता है:

शून्य s_out (PRINTF_INFO * p_inf, char ch)
{
  (* (p_inf-> destptr) ++) = ch;
  p_inf-> परिणाम ++;
}

int vsprintf (char * dest, const char * fmt, va_list args)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = dest;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf (और p_inf, fmt, args);
}

Core_printf फ़ंक्शन तब आउटपुट होने के लिए प्रत्येक वर्ण के लिए p_inf-> func कहता है; आउटपुट फ़ंक्शन तब वर्णों को कंसोल, एक फ़ाइल, एक स्ट्रिंग, या कुछ और में भेज सकता है। यदि किसी का कार्यान्वयन core_printf फ़ंक्शन (और जो भी सेटअप तंत्र इसका उपयोग करता है) को उजागर करता है, तो कोई भी इसे सभी प्रकार की विविधताओं के साथ विस्तारित कर सकता है।


1

एक संभावित तरीका #define का उपयोग करना है:

#define exampleB(int b, ...)  example(0, b, __VA_ARGS__)

0

उस टिप्पणी के आधार पर, जिसे आप रैप कर रहे हैं vsprintf, और यह C ++ के रूप में टैग किया गया है, मैं आपको ऐसा करने का प्रयास नहीं करने का सुझाव दूंगा, लेकिन इसके बजाय C ++ iostreams का उपयोग करने के लिए अपने इंटरफ़ेस को बदल दें। उनके पास printफ़ंक्शंस की लाइन पर फ़ायदे हैं , जैसे कि टाइप सेफ्टी और printfउन आइटम्स को प्रिंट करने में सक्षम होना जो संभाल नहीं पाएंगे। कुछ rework अब भविष्य में एक महत्वपूर्ण राशि के दर्द को बचा सकता है।


आप किन फायदों की बात कर रहे हैं?
cjcurrie

@cjcurrie: लाभ प्रकार की सुरक्षा है, यहां तक ​​कि उपयोगकर्ता-परिभाषित प्रकारों के साथ भी। C फ़ंक्शन उपयोगकर्ता-परिभाषित प्रकारों को बिल्कुल भी नहीं संभाल सकता है।
जोनाथन लेफ़लर

-1

नए C ++ 0x मानक का उपयोग करते हुए, आप इसे वैरिएड टेम्प्लेट का उपयोग करके प्राप्त करने में सक्षम हो सकते हैं या उस पुराने कोड को बिना तोड़-फोड़ के नए टेम्प्लेट सिंटैक्स में परिवर्तित कर सकते हैं।


दुर्भाग्य से यह सभी मामलों में संभव नहीं है - बस
serup

-1

यह करने का एकमात्र तरीका है .. और इसे करने का सबसे अच्छा तरीका भी ।।

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
    BOOL res;

    va_list vl;
    va_start(vl, format);

    // Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
    uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
    printf("arg count = %d\n", argCount);

    // ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
    __asm
    {
        mov eax, argCount
        test eax, eax
        je noLoop
        mov edx, vl
        loop1 :
        push dword ptr[edx + eax * 4 - 4]
        sub eax, 1
        jnz loop1
        noLoop :
        push format
        push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
        mov eax, OriginalVarArgsFunction
        call eax
        mov res, eax
        mov eax, argCount
        lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
        add esp, eax
    }
    return res;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.