Perché non dovremmo mai "asserire" che una chiamata alla libreria non fallisce con un certo codice di errore?

4

Da man 3 assert_perror :

The purpose of the assert macros is to help programmers find bugs in their programs, things that cannot happen unless there was a coding mistake. However, with system or library calls the situation is rather different, and error returns can happen, and will happen, and should be tested for. Not by an assert, where the test goes away when NDEBUG is defined, but by proper error handling code. Never use this macro.

Trovo difficile comprendere la logica di cui sopra.

Certo, è abbastanza ovvio per i alcuni codici di errore. Ad esempio, secondo man 2 recv , questa funzione può fallire con EINTR quando " la ricezione è stata interrotta dall'erogazione di un segnale prima che i dati fossero disponibili ". Per certi versi, l'uso di assert o assert_perror per testare questo codice di errore nella maggior parte dei casi sembra essere una pessima idea.

Tuttavia, recv() può anche fallire con EFAULT se " il puntatore / i del buffer di ricezione puntano al di fuori dello spazio di indirizzamento del processo ". Una simile situazione chiaramente " non può accadere a meno che non ci sia un errore di codifica ", quindi lo scopo principale di testare questo errore sembra essere " per aiutare i programmatori a trovare bug nei loro programmi ”. Nonostante ciò che dice man 3 assert_perror , non riesco a vedere come la situazione di recv() fallendo con EFAULT possa essere " piuttosto diversa " da qualsiasi altro bug che può essere testato con una macro assert() .

Certo, in questo caso, proprio come in qualsiasi altra situazione in cui è stato rilevato un bug chiaro in fase di esecuzione, il programma dovrebbe probabilmente interrompersi. Ma questo vale anche per qualsiasi altro bug. Se solleviamo questo argomento contro l'uso di assert_perror in una situazione del genere, condanniamo automaticamente l'uso di assert() per qualsiasi test non pesante per il calcolo a favore dell'esecuzione di questo test in fase di esecuzione e terminando il programma in questo test fallire.

Perché dovremmo mai utilizzare la macro assert_perror o testare le funzioni della libreria che non riescono con determinati codici di errore con la macro assert ?

    
posta gaazkam 28.04.2017 - 20:54
fonte

3 risposte

7

Il consiglio è dato dal fatto che assert_perror non genera alcun codice di controllo degli errori in NDEBUG , e, la logica è che non vuoi che i siti di chiamata di libreria / sistema vadano controllati in alcune condizioni di costruzione e deselezionati in altri.

Credo che il timore è che i programmatori utilizzino assert_perror(err); e che in caso contrario non abbiano alcun controllo aggiuntivo degli errori, il che significa che quando NDEBUG è definito, non ci sarà alcun controllo degli errori in tali siti di chiamate di libreria / sistema .

Siccome assert_perror controlla tutti gli errori, non solo quelli come EFAULT , gli autori ritengono che non ci siano casi d'uso validi per questo controllo di errore generale condizionale (solo sotto non- NDEBUG ).

In altre parole, è necessario verificare eventuali errori dopo una chiamata di libreria / sistema e, in caso di errore, non procedere come se la chiamata fosse riuscita. Pertanto, non vi è alcun valore aggiuntivo in aggiunta a assert_perror oltre al normale codice di gestione degli errori.

Questo stesso ammonimento non si applica al più generale assert , poiché si presume che i programmatori usino solo quelli per trovare i bug prima del rilascio alla produzione e non per una corretta gestione degli errori.

Ci sono ancora casi d'uso per cercare bug nella produzione con software complicato, tuttavia, e una grande azienda di software che conosco usa assert e noWayAssert ; dove ex vengono eletti da flag (come NDEBUG ) e questi ultimi non vengono mai rimossi nemmeno in produzione.

    
risposta data 28.04.2017 - 21:15
fonte
3

Gli avvisi sono lì per trovare errori di programmazione.

Si presume che un certo errore in una chiamata alla libreria non accadrà mai. Questa ipotesi può essere giusta o sbagliata. Se è sbagliato, allora hai un errore di programmazione. Si utilizza un'asserzione per trovare l'errore di programmazione (se tale errore esiste). Quindi un'asserzione per verificare un errore che si presume non accada è del tutto corretta.

"Non dovresti mai usare affermare in questa situazione" è sbagliato. Innanzitutto, è meglio che non usare affatto. In secondo luogo, l'autore ti chiede di scrivere codice di gestione degli errori per una situazione che non è mai avvenuta (come dimostrato dall'asserzione). Poiché non è mai accaduto, il codice di gestione degli errori non è stato testato in una situazione di vita reale e non è possibile sapere come gestire l'errore in modo significativo. Non è possibile testare il codice di gestione degli errori. Non è possibile garantire o addirittura presumere che la gestione dell'errore porterà a un risultato migliore per l'utente piuttosto che non tentare di gestirlo.

    
risposta data 29.04.2017 - 00:51
fonte
1

The purpose of the assert macros is to help programmers find bugs in their programs, things that cannot happen unless there was a coding mistake. However, with system or library calls the situation is rather different, and error returns can happen, and will happen, and should be tested for. Not by an assert, where the test goes away when NDEBUG is defined, but by proper error handling code. Never use this macro.

Correzione: lo scopo delle macro assert è aiutare gli sviluppatori interni a trovare bug nei loro programmi, non sviluppatori esterni. Non aiutano affatto i programmatori esterni se la libreria o il sistema è integrato nel rilascio quando lo usano. Basta seguire la logica per aiutare gli sviluppatori a trovare bug nei loro programmi, affermare che stai usando una libreria di terze parti (una versione binaria) e che inizia a scoppiare. Il problema è nel tuo codice o nel loro?

Se il codice asserisce e non usa la gestione degli errori, non è possibile capirlo molto facilmente. Anche se l'errore interno nella libreria è causato da un errore del programmatore da parte loro, almeno l'uso della gestione degli errori anziché fare assolutamente nulla in tale scenario potrebbe aiutarti, il programmatore in questo caso utilizza la sua libreria , scopri che hanno un bug nel loro codice e non il tuo.

L'aggiunta di "interno" a "programmatore" aiuta molto a pensarci. Il punto di un assert non è quello di trovare semplicemente "errori del programmatore", ma in particolare "errori interni del programmatore". In questo caso un errore di asserzione non può essere necessariamente catturato da questi "utenti esterni" o "programmatori esterni" se il controllo interno viene omesso da una versione di rilascio anziché, ad esempio, mostrando un messaggio di errore che possono vedere indipendentemente da cosa ( in realtà ciò si collega a una domanda precedente che ho ricevuto quando un release_assert potrebbe essere utile: questo sarebbe un caso). Non possono sapere se hanno usato la funzione in modo errato, dal momento che probabilmente stanno usando una build di rilascio di quella libreria. Sono lasciati al buio, per così dire, se tutto ciò che fai è assert in uno scenario del genere. Quindi vogliamo usare un meccanismo di controllo degli errori che non lasci questi "programmatori esterni", o "utenti esterni", al buio contro i loro errori, e le asserzioni non lo fanno (si limitano a informare i "programmatori interni" dei loro errori).

Quindi, per le biblioteche, spesso devi fare la tua parte di un tipo che prova l'illusione di un progetto user-end, perché le persone usano le tue librerie, anche se sono programmatori che scrivono codice contro di esso , sono come "utenti esterni". Non vuoi lasciarli al buio, indovinando cosa è andato storto, se forniscono input errati.

E questo vale anche per me, usando, ad esempio, le API del sistema operativo. Fortunatamente molti di loro non hanno semplicemente assert in modi che non avrei visto (dal momento che sto usando build di rilascio ottimizzati di questi sistemi operativi) quando ho fornito un input non valido. In realtà hanno segnalato errori che sono riuscito a individuare e capire cosa ho fatto di sbagliato. In questo caso sono un "utente esterno" di quella libreria, ed è stato utile che abbia catturato i miei errori per me.

Detto questo, non sono d'accordo con la logica di non usare mai questa macro, dal momento che evitare l'uso di un assert non fa in modo che i programmatori controllino automaticamente tutte le possibili condizioni di errore. Come diceva gnasher729 , è almeno meglio che non usare assert (e non controllare affatto le condizioni di errore).

    
risposta data 25.01.2018 - 12:45
fonte

Leggi altre domande sui tag