web-dev-qa-db-fra.com

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

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

  1. Mettre un throw dans une fonction constexpr
  2. Déclaration d'un problème non résolu extern const char*
  3. Référencement du extern non résolu dans le throw

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?
95
sudo make install

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 fonction malloc afin 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 invocable au moment de l'exécution, donc c'est seulement appelable 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 expression throw 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 de renvoi qui fait référence à un symbole non défini ne sera pas utilisée lors des appels de compilation (car cela ne pourrait pas être compilé) 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 "odr-used" dans les appels de compilation de la fonction, dans la 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 pour le faire, ne disant pas nécessairement que c'est une bonne idée ou largement 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 préoccuper.

Une des raisons pour lesquelles pourrait être utile est que la version de 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). Vous pouvez donc 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 était appelée lors de l'exécution - temps. Vous pouvez empoisonner le sous-optimal pour vous assurer qu'il n'est jamais utilisé pour les appels au moment de l'exécution, en veillant à ce que la version plus efficace (non constexpr) soit utilisée pour les appels au moment de l'exécution.

N.B. 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 temps d'exécution. Cela peut ralentir votre compilation en obligeant le compilateur à faire un travail supplémentaire, mais il n'aura aucun coût de performance à l'exécution.

105
Jonathan Wakely

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

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

17
SergeyA