Hoe maak je een functie met een variabele lengte argumenten wrap?

stemmen
44

Ik ben op zoek om dit te doen in C / C ++.

Ik kwam over variabele lengte Argumenten maar dit suggereert een oplossing met Python & C met behulp van libffi .

Nu, als ik wil wrap printf-functie metmyprintf

Wat ik doe is zoals hieronder:

void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    printf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C';
    myprintf(This is a number: %d and \nthis is a character: %c and \n another number: %d\n,a, v, b);
    return 0;
}

Maar de resultaten zijn niet zoals verwacht!

This is a number: 1244780 and
this is a character: h and
another number: 29953463

Elk punt waar heb ik mis ??

De vraag is gesteld op 03/09/2008 om 09:12
user
In andere talen...                            


7 antwoorden

stemmen
0

Hoe bedoel je een pure C / C ++ oplossing?

De rest parameter (...) wordt ondersteund dwarsplatform in de runtime C.

http://msdn.microsoft.com/en-us/library/kb57fad8.aspx

antwoordde op 03/09/2008 om 09:19
bron van user

stemmen
1

Bent u met behulp van C of C ++? De volgende C ++ versie, C ++ 0x, zal ondersteunen variadic templates , die een oplossing voor dat probleem.

Een andere oplossing kan worden bereikt door slimme operator overbelasting om een ​​syntax als dit te bereiken:

void f(varargs va) {
    BOOST_FOREACH(varargs::iterator i, va)
        cout << *i << " ";
}

f(args = 1, 2, 3, "Hello");

Om dit te laten werken, de klas varargsmoet worden uitgevoerd om te overschrijven operator =dat een proxy-object dat op zijn beurt voorrang geeft operator ,. Echter, het maken van deze variant soort veilig in de huidige C ++ is niet mogelijk voor zover ik weet, omdat het zou moeten werken naar type wissen.

antwoordde op 03/09/2008 om 09:20
bron van user

stemmen
8

Ik ben ook niet zeker wat je bedoelt met pure

In C ++ we gebruiken

#include <cstdarg>
#include <cstdio>

class Foo
{   void Write(const char* pMsg, ...);
};

void Foo::Write( const char* pMsg, ...)
{
    char buffer[4096];
    std::va_list arg;
    va_start(arg, pMsg);
    std::vsnprintf(buffer, 4096, pMsg, arg);
    va_end(arg);
    ...
}
antwoordde op 03/09/2008 om 09:33
bron van user

stemmen
65

het probleem is dat je 'printf' niet gebruiken bij va_args. U moet gebruiken vprintf als u gebruik maakt van variabele argument lijsten. vprint, vsprintf, vfprintf, enz. (er zijn ook 'veilig' versies in C runtime van Microsoft dat wordt voorkomen dat bufferoverruns, etc.)

Je voorbeeld werkt als volgt:

void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    vprintf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
    return 0;
}
antwoordde op 03/09/2008 om 13:30
bron van user

stemmen
0
void myprintf(char* fmt, ...)
{
    va_ list args;
    va_ start(args,fmt);
    printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used.
    va_ end(args);
}
If you're just trying to call printf, 
there's a printf variant called vprintf that takes 
the va_list directly :  vprintf(fmt, args);
antwoordde op 30/04/2012 om 06:55
bron van user

stemmen
9

In C ++ 11 is dit een mogelijke oplossing gebruikt Variadic templates:

template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
    std::printf( fmt, args... ) ;
}

BEWERK

Zoals @rubenvb wijst er trade-offs te overwegen, bijvoorbeeld zult u het genereren van code voor elke instantie die zal leiden tot code bloat.

antwoordde op 24/07/2013 om 13:45
bron van user

stemmen
3

Eigenlijk is er een manier om een functie die geen heeft noemen va_listversie van een wrapper. Het idee is om assembler gebruiken, argumenten niet aanraken in de stack, en tijdelijke vervanging van de functie retouradres.

Voorbeeld van Visual C x86. call addr_printfnoemt printf():

__declspec( thread ) static void* _tls_ret;

static void __stdcall saveret(void *retaddr) {
    _tls_ret = retaddr;
}

static void* __stdcall _getret() {
    return _tls_ret;
}

__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
    __asm {
        call _getret
        mov [esp], eax   ; /* replace current retaddr with saved */
        mov eax, [esp+4] ; /* retval */
        ret 4
    }
}

static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
    printf("calling printf(\"%s\")\n", fmt);
}

static void __stdcall _dbg_printf_end(int ret) {
    printf("printf() returned %d\n", ret);
}

__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
    static const void *addr_printf = printf;
    /* prolog */
    __asm {
        push ebp
        mov  ebp, esp
        sub  esp, __LOCAL_SIZE
        nop
    }
    {
        va_list args;
        va_start(args, fmt);
        _dbg_printf_beg(fmt, args);
        va_end(args);
    }
    /* epilog */
    __asm {
        mov  esp, ebp
        pop  ebp
    }
    __asm  {
        call saveret
        call addr_printf
        push eax
        push eax
        call _dbg_printf_end
        call restret_and_return_int
    }
}
antwoordde op 26/02/2014 om 15:01
bron van user

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more