Passaggio di argomenti variabili a un'altra funzione che accetta un elenco di argomenti variabili


114

Quindi ho 2 funzioni che hanno entrambi argomenti simili

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

Ora examplechiama exampleB, ma come posso passare le variabili nell'elenco degli argomenti delle variabili senza modificare exampleB(poiché questo è già utilizzato anche altrove).



2
Bene, la soluzione su quello stava usando vprintf, e non è questo il caso.
Non disponibile

Questo è correlato, ma sicuramente non è lo stesso, del duplicato proposto: inoltrare un'invocazione di una funzione variadica in C?
Jonathan Leffler

Risposte:


127

Non puoi farlo direttamente; devi creare una funzione che accetta 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
Sospetto di dover fare qualcosa del genere, il problema è che la funzione di esempio è fondamentalmente un wrapper per vsprintf e non molto altro: /
Non disponibile

@Xeross: Notare che questo non cambia la specifica esterna di ciò che fa exampleB - cambia solo l'implementazione interna. Non sono sicuro di quale sia il problema.
Jonathan Leffler

Il primo parametro è obbligatorio o tutti i parametri possono essere variadici?
Qwerty

1
@Qwerty: la sintassi richiede un primo argomento con nome per una funzione che accetta un elenco di argomenti variabili con , ...nella firma. Se hai convertito gli argomenti della variabile in a va_list, puoi passare va_lista un'altra funzione che accetta solo a va_list, ma quella funzione (o quella che chiama) deve avere un modo per sapere cosa c'è nel file va_list.
Jonathan Leffler

46

Forse lanciare una roccia in uno stagno qui, ma sembra funzionare abbastanza bene con i modelli variadici 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);
}

Per lo meno, funziona con g++.


Sono confuso: è un approccio legittimo che utilizza C ++> = 11?
Duncan Jones

@DuncanJones Sì, il pacchetto viene ampliato daargs...
Swordfish

15

dovresti creare versioni di queste funzioni che prendono una va_list e passarle. Guarda vprintfcome esempio:

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

5

Volevo anche avvolgere printf e ho trovato una risposta utile qui:

Come passare un numero variabile di argomenti a printf / sprintf

Non ero affatto interessato alle prestazioni (sono sicuro che questo pezzo di codice può essere migliorato in molti modi, sentiti libero di farlo :)), questo è solo per la stampa di debug generale, quindi ho fatto questo:

//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;
}

che poi posso usare in questo modo

Point2d p;

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

Gli ostream in c ++ sono belli sotto alcuni aspetti, ma praticamente diventano orribili se vuoi stampare qualcosa del genere con alcune piccole stringhe come parentesi, due punti e virgole inserite tra i numeri.


2

Per inciso, molte implementazioni C hanno una variazione interna di v? Printf che IMHO avrebbe dovuto far parte dello standard C. I dettagli esatti variano, ma un'implementazione tipica accetterà una struttura contenente un puntatore a funzione di output dei caratteri e informazioni che dicono cosa dovrebbe accadere. Ciò consente a printf, sprintf e fprintf di utilizzare tutti lo stesso meccanismo "core". Ad esempio, vsprintf potrebbe essere qualcosa di simile:

void s_out (PRINTF_INFO * p_inf, char ch)
{
  (* (p_inf-> destptr) ++) = ch;
  p_inf-> Risultati ++;
}

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);
}

La funzione core_printf chiama quindi p_inf-> func per ogni carattere da visualizzare; la funzione di output può quindi inviare i caratteri alla console, un file, una stringa o qualcos'altro. Se la propria implementazione espone la funzione core_printf (e qualunque meccanismo di setup utilizzi), è possibile estenderla con ogni sorta di variazioni.


1

Un modo possibile è usare #define:

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

0

In base al commento che stai avvolgendo vsprintfe che questo è etichettato come C ++, suggerirei di non provare a farlo, ma di cambiare la tua interfaccia per usare invece iostream C ++. Hanno vantaggi rispetto alla printlinea di funzioni, come la protezione dai tipi e la possibilità di stampare elementi che printfnon sarebbero in grado di gestire. Alcune rielaborazioni ora potrebbero risparmiare una notevole quantità di dolore in futuro.


A quali vantaggi ti riferisci?
cjcurrie

@cjcurrie: il vantaggio è l'indipendenza dai tipi, anche con i tipi definiti dall'utente. Le funzioni C non possono gestire affatto i tipi definiti dall'utente, ovviamente.
Jonathan Leffler

-1

Usando il nuovo standard C ++ 0x, potresti essere in grado di farlo usando modelli variadici o persino convertire quel vecchio codice nella nuova sintassi del modello senza rompere nulla.


sfortunatamente questo non è possibile in tutti i casi - prova a usare
lambdas

-1

Questo è l'unico modo per farlo .. e anche il modo migliore per farlo ..

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.