Domanda

Ho questo pezzo di codice (riassunto) ...

AnsiString working(AnsiString format,...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

E, sulla base del fatto che è preferibile passare per riferimento ove possibile, l'ho modificato in questo modo.

AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}

Il mio codice chiamante è così: -

AnsiString s1, s2;
    s1 = working("Hello %s", "World");
    s2 = broken("Hello %s", "World");

Ma, s1 contiene " Hello World " ;, mentre s2 ha " Hello (null) " ;. Penso che ciò sia dovuto al modo in cui va_start funziona, ma non sono esattamente sicuro di cosa stia succedendo.

È stato utile?

Soluzione

Se guardi a cosa va_start si espande, vedrai cosa sta succedendo:

va_start(argptr, format); 

diventa (approssimativamente)

argptr = (va_list) (&format+1);

Se il formato è un tipo di valore, viene inserito nello stack proprio prima di tutti gli argomenti variadici. Se il formato è un tipo di riferimento, solo l'indirizzo viene inserito nello stack. Quando prendi l'indirizzo della variabile di riferimento, ottieni l'indirizzo o la variabile originale (in questo caso di una AnsiString temporanea creata prima di chiamare Broken), non l'indirizzo dell'argomento.

Se non vuoi passare in giro per le classi complete, le tue opzioni sono passare il puntatore o inserire un argomento fittizio:

AnsiString working_ptr(const AnsiString *format,...)
{
    ASSERT(format != NULL);
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format->c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");

o

AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, dummy);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

s1 = working_dummy("Hello %s", 0, "World");

Altri suggerimenti

Ecco cosa dice lo standard C ++ (18.7 - Altro supporto di runtime) su va_start() (l'enfasi è mia):

  

Le restrizioni imposte da ISO C   il secondo parametro a   <stdarg.h> macro nell'intestazione   parmN sono diversi in questo   Standard internazionale. Il parametro   ... è l'identificatore di   parametro più a destra nella variabile   elenco dei parametri della funzione   definizione (quella appena prima del   <=>).    Se il parametro <=> è dichiarato con una funzione, un array o un riferimento   tipo, o con un tipo che non lo è   compatibile con il tipo che risulta   quando si passa un argomento per il quale   non esiste alcun parametro, il comportamento   non è definito .

Come altri hanno già detto, l'uso di varargs in C ++ è pericoloso se lo si utilizza con elementi non straight-C (e possibilmente anche in altri modi).

Detto questo, uso ancora printf () tutto il tempo ...

Una buona analisi del motivo per cui non vuoi questo si trova in N0695

Secondo gli standard di codifica C ++ (Sutter, Alexandrescu):

varargs non dovrebbe mai essere usato con C ++:

Non sono sicuri per i tipi e hanno un comportamento NON DEFINITO per oggetti di tipo classe, che probabilmente sta causando il tuo problema.

Ecco la mia soluzione semplice (compilata con Visual C ++ 2010):

void not_broken(const string& format,...)
{
  va_list argptr;
  _asm {
    lea eax, [format];
    add eax, 4;
    mov [argptr], eax;
  }

  vprintf(format.c_str(), argptr);
}

Nota a margine:

Il comportamento dei tipi di classe come argomenti varargs potrebbe non essere definito, ma è coerente nella mia esperienza. Il compilatore inserisce nello stack la dimensione della (classe) della memoria della classe. Cioè, in pseudo-codice:

alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);

Per un esempio davvero interessante di questo utilizzo in modo molto creativo, nota che puoi passare un'istanza CString al posto di un LPCTSTR a una funzione varargs direttamente, e funziona, e c'è nessun casting coinvolto. Lascio che sia un esercizio per il lettore capire come hanno fatto a funzionare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top