Que signifie «empoisonner une fonction» en C ++?

96

À la toute fin du discours de Scott Schurr "Présentation constexpr" à la CppCon , il demande "Y a-t-il un moyen d'empoisonner une fonction"? Il explique ensuite que cela peut être fait (bien que de manière non standard) en:

  1. Mettre un throwdans une constexprfonction
  2. Déclarer un non résolu extern const char*
  3. Référencer les non résolus externdans lethrow

Je sens que je suis un peu hors de ma profondeur ici, mais je suis curieux:

  • Que signifie «empoisonner une fonction»?
  • Quelle est la signification / l'utilité de la technique qu'il décrit?
sudo faire installer
la source
1
Jamais entendu parler de ce terme, clarifiez avec un exemple concis s'il vous plaît!
πάντα ῥεῖ
6
@ πάνταῥεῖ, je viens de clarifier. C'est un terme `` largement connu dans les petits cercles ''
SergeyA
4
Il parle de s'assurer que chaque appel à la constexprfonction est évalué au moment de la compilation.
TC
@TC Right - il a mentionné qu'une constexprfonction pouvait être utilisée soit au moment de la compilation, soit au moment de l'exécution. C'est donc un moyen de le forcer afin que vous ne puissiez pas l'utiliser au moment de l'exécution? Quand est-ce utile?
sudo make install
3
Surtout en C ++ 11, une constexprfonction n'est souvent pas l'implémentation la plus efficace à cause des contraintes, donc on peut ne pas vouloir qu'elle soit évaluée au moment de l'exécution; ou, c'est peut-être le cas d'erreur (comme dans son exemple).
TC

Réponses:

106

En général, il s'agit de rendre une fonction inutilisable, par exemple si vous voulez interdire l'utilisation de l'allocation dynamique dans un programme, vous pouvez "empoisonner" la mallocfonction pour qu'elle ne puisse pas être utilisée.

Dans la vidéo, il l'utilise d'une manière plus spécifique, ce qui est clair si vous lisez la diapositive qui s'affiche quand il parle d'empoisonner la fonction, qui dit "Un moyen de forcer la compilation uniquement?"

Il parle donc d '"empoisonner" la fonction pour la rendre impossible à exécuter au moment de l'exécution, donc elle ne peut être appelée que dans des expressions constantes. La technique consiste à avoir une branche dans la fonction qui n'est jamais prise lorsqu'elle est appelée dans un contexte de compilation, et à faire en sorte que cette branche contienne quelque chose qui provoquera une erreur.

Une throwexpression est autorisée dans une fonction constexpr, tant qu'elle n'est jamais atteinte lors des appels de la fonction au moment de la compilation (car vous ne pouvez pas lever d'exception au moment de la compilation, c'est une opération intrinsèquement dynamique, comme l'allocation de mémoire). Ainsi, une expression throw qui fait référence à un symbole non défini ne sera pas utilisée lors des appels au moment de la compilation (car la compilation échouerait) et ne peut pas être utilisée au moment de l'exécution, car le symbole non défini provoque une erreur de l'éditeur de liens.

Étant donné que le symbole non défini n'est pas "utilisé par odr" dans les appels de la fonction au moment de la compilation, en pratique, le compilateur ne créera pas de référence au symbole, il est donc normal qu'il ne soit pas défini.

Est-ce utile? Il montre comment le faire, sans nécessairement dire que c'est une bonne idée ou très utile. Si vous avez besoin de le faire pour une raison quelconque, sa technique pourrait résoudre votre problème. Si vous n'en avez pas besoin, vous n'avez pas à vous en soucier.

Une des raisons pour lesquelles cela peut être utile est lorsque la version à la compilation d'une opération n'est pas aussi efficace qu'elle pourrait l'être. Il existe des restrictions sur le type d'expressions autorisées dans une fonction constexpr (en particulier en C ++ 11, certaines restrictions ont été supprimées en C ++ 14). Ainsi, vous pouvez avoir deux versions d'une fonction pour effectuer un calcul, une qui est optimale, mais utilise des expressions qui ne sont pas autorisées dans une fonction constexpr, et une qui est une fonction constexpr valide, mais qui fonctionnerait mal si elle est appelée à l'exécution- temps. Vous pouvez empoisonner la version sous-optimale pour vous assurer qu'elle n'est jamais utilisée pour les appels d'exécution, en vous assurant que la version la plus efficace (non-constexpr) est utilisée pour les appels d'exécution.

NB Les performances d'une fonction constexpr utilisée au moment de la compilation ne sont pas vraiment importantes, car elle n'a de toute façon pas de surcharge d'exécution. Cela peut ralentir votre compilation en obligeant le compilateur à faire un travail supplémentaire, mais cela n'aura aucun coût en termes de performances d'exécution.

Jonathan Wakely
la source
1
J'ai lu le texte de la diapositive, mais je n'ai pas vu le lien avec le terme qu'il utilisait. C'est évident maintenant que vous l'avez expliqué, mais je ne l'ai pas vu à l'époque. Merci beaucoup pour cette excellente réponse - j'adore ce site Web.
sudo make install
@PravasiMeet, posez votre propre question, ne détournez pas les commentaires de la question de quelqu'un d'autre sur quelque chose de différent. Une solution simple serait de le définir comme supprimé dans chaque unité de traduction, ou de le remplacer par votre propre définition faisant référence à un symbole non défini.
Jonathan Wakely
17

«Empoisonner» un identificateur signifie que toute référence à l'identificateur après «l'empoisonnement» est une erreur de compilation matérielle. Cette technique peut être utilisée, par exemple, pour la dépréciation définitive (la fonction est obsolète, ne l'utilisez jamais!).

Dans GCC traditionnellement il y avait un pragma pour cela: #pragma GCC poison.

SergeyA
la source
1
Oui, mais pas tout à fait dans le sens utilisé dans cet exposé.
TC
@TC, ok, je devrais probablement le regarder avant de répondre :)
SergeyA