Cosa significa "avvelenare una funzione" in C ++?


96

Alla fine del discorso di Scott Schurr "Introducing constexpr" al CppCon , chiede "C'è un modo per avvelenare una funzione"? Spiega quindi che questo può essere fatto (anche se in modo non standard):

  1. Mettere un throwin una constexprfunzione
  2. Dichiarare un irrisolto extern const char*
  3. Riferimento all'irrisolto externinthrow

Sento di essere un po 'fuori dalla mia profondità qui, ma sono curioso:

  • Cosa significa "avvelenare una funzione"?
  • Qual è il significato / utilità della tecnica che delinea?

1
Mai sentito parlare di quel termine, chiarisci con un esempio conciso per favore!
πάντα ῥεῖ

6
@ πάνταῥεῖ, ho appena chiarito. Questo è un termine "ampiamente conosciuto in piccoli circoli"
SergeyA

4
Sta parlando di garantire che ogni chiamata alla constexprfunzione venga valutata in fase di compilazione.
TC

@TC Right - ha affermato che una constexprfunzione potrebbe essere utilizzata in fase di compilazione o in fase di esecuzione. Quindi questo è un modo per forzarlo in modo da non poterlo usare in fase di esecuzione? Quando è utile?
sudo fa installare il

3
Soprattutto in C ++ 11, una constexprfunzione spesso non è l'implementazione più efficiente a causa dei vincoli, quindi si potrebbe non volerla valutare in fase di esecuzione; o forse è il caso di errore (come nel suo esempio).
TC

Risposte:


106

In generale si riferisce a rendere inutilizzabile una funzione, ad esempio se si desidera vietare l'uso dell'allocazione dinamica in un programma si potrebbe "avvelenare" la mallocfunzione in modo che non possa essere utilizzata.

Nel video lo sta usando in un modo più specifico, il che è chiaro se leggi la diapositiva che viene visualizzata quando parla di avvelenamento della funzione, che dice "Un modo per forzare solo il tempo di compilazione?"

Quindi sta parlando di "avvelenare" la funzione per renderla non richiamabile in fase di esecuzione, quindi è richiamabile solo in espressioni costanti. La tecnica consiste nell'avere un ramo nella funzione che non viene mai preso quando viene chiamato in un contesto in fase di compilazione, e fare in modo che quel ramo contenga qualcosa che causerà un errore.

throwUn'espressione è consentita in una funzione constexpr, fintanto che non viene mai raggiunto durante invocazioni di compilazione della funzione (perché non si può generare un'eccezione al momento della compilazione, è un'operazione intrinsecamente dinamica, come l'allocazione di memoria). Quindi un'espressione di lancio che fa riferimento a un simbolo non definito non verrà utilizzata durante le chiamate in fase di compilazione (perché non verrebbe compilata) e non può essere utilizzata in fase di esecuzione, perché il simbolo non definito causa un errore del linker.

Poiché il simbolo indefinito non è "odr-usato" nelle invocazioni della funzione in fase di compilazione, in pratica il compilatore non creerà un riferimento al simbolo, quindi va bene che sia indefinito.

È utile? Sta dimostrando come farlo, non necessariamente dicendo che è una buona idea o ampiamente utile. Se hai bisogno di farlo per qualche motivo, la sua tecnica potrebbe risolvere il tuo problema. Se non ne hai bisogno, non devi preoccupartene.

Uno dei motivi per cui potrebbe essere utile è quando la versione in fase di compilazione di alcune operazioni non è efficiente come potrebbe essere. Esistono restrizioni sul tipo di espressioni consentite in una funzione constexpr (specialmente in C ++ 11, alcune restrizioni sono state rimosse in C ++ 14). Quindi potresti avere due versioni di una funzione per eseguire un calcolo, una che è ottimale, ma utilizza espressioni che non sono consentite in una funzione constexpr, e una che è una funzione constexpr valida, ma funzionerebbe male se chiamata in esecuzione- tempo. È possibile avvelenare quello non ottimale per assicurarsi che non venga mai utilizzato per le chiamate in fase di esecuzione, assicurandosi che la versione più efficiente (non constexpr) venga utilizzata per le chiamate in fase di esecuzione.

NB Le prestazioni di una funzione constexpr usata in fase di compilazione non sono molto importanti, perché comunque non ha alcun overhead di runtime. Potrebbe rallentare la compilazione facendo eseguire al compilatore un lavoro extra, ma non avrà alcun costo in termini di prestazioni in fase di esecuzione.


1
Ho letto il testo della diapositiva, ma non ho visto la connessione al termine che stava usando. È ovvio ora che l'hai spiegato, ma all'epoca non l'ho visto. Grazie mille per questa eccellente risposta - Adoro questo sito web.
sudo make install

@PravasiMeet, fai la tua domanda, non dirottare i commenti della domanda di qualcun altro su qualcosa di diverso. Una soluzione semplice sarebbe definirlo come cancellato in ogni unità di traduzione o sostituirlo con la tua definizione che fa riferimento a un simbolo indefinito.
Jonathan Wakely

17

"Poisoning" un identificatore significa che qualsiasi riferimento all'identificatore dopo l '"avvelenamento" è un errore del compilatore. Questa tecnica può essere utilizzata, ad esempio, per deprecazioni pesanti (la funzione È deprecata, non usarla mai!).

Nel GCC tradizionalmente c'era un pragma per questo: #pragma GCC poison.


1
Sì, ma non proprio nel senso usato in quel discorso.
TC

@TC, ok, probabilmente dovrei guardarlo prima di rispondere :)
SergeyA
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.