2013-04-15 13 views
5

Esistono diverse implementazioni della funzione printf dei modelli variadici. Uno è questo:Modelli variabili e sicurezza tecnica

void printf(const char* s) { 
    while (*s) { 
    if (*s == '%' && *++s != '%') 
     throw std::runtime_error("invalid format string: missing arguments"); 
    std::cout << *s++; 
    } 
} 

template<typename T, typename... Args> 
void printf(const char* s, const T& value, const Args&... args) { 
    while (*s) { 
    if (*s == '%' && *++s != '%') { 
     std::cout << value; 
     return printf(++s, args...); 
    } 
    std::cout << *s++; 
    } 
    throw std::runtime_error("extra arguments provided to printf"); 
} 

e ovunque è detto che questa implementazione è di tipo-sicuro, mentre la C normale (con argomenti variadic va_arg) non lo è.

Perché è quello? Che cosa significa essere sicuro per il tipo e quali vantaggi ha questa implementazione su un C printf va_arg uno?

+3

Questa versione non si preoccupa affatto dei flag di formato, ma stampa le cose attraverso gli operatori di streaming. – Xeo

+1

È sicuro dal tipo in cui "T" sarà sempre il tipo di parametro effettivamente passato. La stampa standard non lo sa. –

+1

Per inciso, si tratta di un'implementazione orribile 'printf'.Ignora e interpreta erroneamente gli specificatori di formato e non supporta nemmeno lo spostamento di valori temporanei! In breve, è una funzione tipizzata, ma non è un'implementazione valida di 'printf', nonostante il suo nome. Un buon 'tipf' printf' si comporterebbe in modo identico a' printf' quando era sicuro, e non sarebbe riuscito a farlo indefinito quando era nella maggior parte dei casi non sicuri. – Yakk

risposta

4

Essere sicuro, o type-safe, significa che si può dire da guardando il codice sorgente di se il programma si comporta correttamente.

La dichiarazione std::cout << x è sempre corretta, supponendo che x abbia un valore ben definito (e non sia, diciamo, non inizializzato); questo è qualcosa che puoi garantire guardando il codice sorgente.

Per constrast, C è non sicura: Per esempio, il seguente codice può essere o non essere corretti, seconda dell'ingresso runtime:

int main(int argc, char * argv[]) 
{ 
    if (argc == 3) 
     printf(argv[1], argv[2]); 
} 

Ciò è corretto se e solo se il il primo argomento è una stringa di formato valida contenente precisamente uno "%s".

In altre parole, è possibile scrivere un programma C corretto, ma è impossibile ragionare sulla correttezza semplicemente controllando il codice. La funzione printf è uno di questi esempi. Più in generale, qualsiasi funzione che accetta argomenti variabili è probabilmente non sicura, così come qualsiasi funzione che esegue il puntamento dei puntatori in base ai valori di runtime.

+0

+1 Difficile superare questo esempio per un migliore stato di esecuzione dipendente dall'input in fase di esecuzione. E un'ottima risposta oltre a ciò. – WhozCraig

5

Per tutti gli argomenti passati alla versione del modello variadic, i loro tipi sono noti al momento della compilazione. Questa conoscenza è conservata all'interno della funzione. Ogni oggetto viene quindi passato a cout con il sovraccarico intenso operator<<. Per ogni tipo che viene passato, c'è un sovraccarico separato di questa funzione. In altre parole, se si passa uno , si chiama ostream::operator<<(int), se si passa un doppio, si chiama ostream::operator<<(double). Quindi, di nuovo, il tipo è conservato. E ognuna di queste funzioni è specializzata per gestire ogni tipo in modo appropriato. Questo è tipo di sicurezza.

Con il C printf, tuttavia, la storia è diversa. Il tipo non è conservato all'interno della funzione. Ha bisogno di capirlo in base al contenuto della stringa di formato (che può essere un valore di runtime). La funzione deve solo supporre che sia stata passata una stringa di formato corretta per abbinare i tipi di argomento. Il compilatore non impone questo.

C'è anche un altro tipo di sicurezza, e questo è nel numero di argomenti. Se si passano troppo pochi argomenti alla funzione C printf, non abbastanza per corrispondere alla stringa di formato, si ha un comportamento indefinito. Se si fa la stessa cosa con il modello variadico, si ottiene un'eccezione, che, sebbene non desiderabile, è un problema molto più facile da diagnosticare.

Problemi correlati