Was bedeutet es, eine Funktion in C ++ zu vergiften?

96

Ganz am Ende von Scott Schurrs Vortrag "Introducing constexpr" auf der CppCon fragt er: "Gibt es eine Möglichkeit, eine Funktion zu vergiften?" Er erklärt dann, dass dies (wenn auch nicht auf standardmäßige Weise) getan werden kann durch:

  1. Einfügen throweiner constexprFunktion
  2. Ungelöst erklären extern const char*
  3. Referenzierung der ungelösten externin derthrow

Ich spüre, dass ich hier etwas überfordert bin, aber ich bin neugierig:

  • Was bedeutet es, "eine Funktion zu vergiften"?
  • Welche Bedeutung / Nützlichkeit hat die von ihm skizzierte Technik?
sudo make install
quelle
1
Noch nie von diesem Begriff gehört, bitte mit einem kurzen Beispiel erläutern!
πάντα ῥεῖ
6
@ πάνταῥεῖ habe ich gerade geklärt. Dies ist ein Begriff "in kleinen Kreisen weithin bekannt"
SergeyA
4
Er spricht davon, sicherzustellen, dass jeder Aufruf der constexprFunktion zur Kompilierungszeit ausgewertet wird.
TC
@TC Richtig - er erwähnte, dass eine constexprFunktion entweder zur Kompilierungszeit oder zur Laufzeit verwendet werden könnte. Dies ist also eine Möglichkeit, es zu erzwingen, damit Sie es zur Laufzeit nicht verwenden können? Wann ist das sinnvoll?
Sudo Make Install
3
Insbesondere in C ++ 11 ist eine constexprFunktion aufgrund der Einschränkungen häufig nicht die effizienteste Implementierung, sodass sie möglicherweise nicht zur Laufzeit ausgewertet werden soll. oder vielleicht ist es der Fehlerfall (wie in seinem Beispiel).
TC

Antworten:

106

Im Allgemeinen bezieht es sich darauf, eine Funktion unbrauchbar zu machen. Wenn Sie beispielsweise die Verwendung der dynamischen Zuordnung in einem Programm verbieten möchten, können Sie die mallocFunktion "vergiften" , sodass sie nicht verwendet werden kann.

In dem Video verwendet er es auf eine spezifischere Art und Weise. Dies wird deutlich, wenn Sie die Folie lesen, die angezeigt wird, wenn er über die Vergiftung der Funktion spricht. Dort heißt es: "Eine Möglichkeit, nur die Kompilierungszeit zu erzwingen?"

Er spricht also davon, die Funktion zu "vergiften", um sie zur Laufzeit nicht mehr abzurufen, sodass sie nur in konstanten Ausdrücken aufgerufen werden kann. Die Technik besteht darin, einen Zweig in der Funktion zu haben, der beim Aufrufen in einem Kontext zur Kompilierungszeit niemals verwendet wird, und diesen Zweig etwas zu enthalten, das einen Fehler verursacht.

Ein throwAusdruck ist in einer constexpr-Funktion zulässig, solange er während der Aufrufe der Funktion zur Kompilierungszeit nie erreicht wird (da Sie zur Kompilierungszeit keine Ausnahme auslösen können, handelt es sich um eine inhärent dynamische Operation wie das Zuweisen von Speicher). Ein Wurfausdruck, der sich auf ein undefiniertes Symbol bezieht, wird daher bei Aufrufen zur Kompilierungszeit nicht verwendet (da dies nicht kompiliert werden kann) und kann zur Laufzeit nicht verwendet werden, da das undefinierte Symbol einen Linkerfehler verursacht.

Da das undefinierte Symbol in den Aufrufen der Funktion zur Kompilierungszeit nicht "odr-verwendet" wird, erstellt der Compiler in der Praxis keinen Verweis auf das Symbol, sodass es in Ordnung ist, dass es undefiniert ist.

Ist das nützlich? Er demonstriert, wie es geht, und sagt nicht unbedingt, dass es eine gute Idee oder allgemein nützlich ist. Wenn Sie es aus irgendeinem Grund tun müssen, kann seine Technik Ihr Problem lösen. Wenn Sie es nicht brauchen, müssen Sie sich darüber keine Sorgen machen.

Ein Grund dafür kann sein, dass die Version eines Vorgangs zur Kompilierungszeit nicht so effizient ist, wie sie sein könnte. Es gibt Einschränkungen hinsichtlich der Art der Ausdrücke, die in einer constexpr-Funktion zulässig sind (insbesondere in C ++ 11 wurden einige Einschränkungen in C ++ 14 entfernt). Sie haben also möglicherweise zwei Versionen einer Funktion zum Durchführen einer Berechnung, eine, die optimal ist, aber Ausdrücke verwendet, die in einer constexpr-Funktion nicht zulässig sind, und eine, die eine gültige constexpr-Funktion ist, aber bei einem Aufruf bei run- eine schlechte Leistung erbringt. Zeit. Sie können das suboptimale vergiften, um sicherzustellen, dass es niemals für Laufzeitanrufe verwendet wird, und um sicherzustellen, dass die effizientere Version (ohne Constexpr) für Laufzeitanrufe verwendet wird.

Hinweis: Die Leistung einer zur Kompilierungszeit verwendeten constexpr-Funktion ist nicht wirklich wichtig, da sie ohnehin keinen Laufzeitaufwand hat. Es kann Ihre Kompilierung verlangsamen, indem der Compiler zusätzliche Arbeit leistet, aber es entstehen keine Kosten für die Laufzeitleistung.

Jonathan Wakely
quelle
1
Ich habe den Text der Folie gelesen, aber den Zusammenhang mit dem von ihm verwendeten Begriff nicht gesehen. Es ist jetzt offensichtlich, dass Sie es erklärt haben, aber ich habe es damals nicht gesehen. Vielen Dank für diese hervorragende Antwort - ich liebe diese Website einfach.
Sudo Make Install
@PravasiMeet, stellen Sie Ihre eigene Frage, entführen Sie nicht die Kommentare der Frage eines anderen zu etwas anderem. Eine einfache Lösung wäre, es in jeder Übersetzungseinheit als gelöscht zu definieren oder durch eine eigene Definition zu ersetzen, die auf ein undefiniertes Symbol verweist.
Jonathan Wakely
17

'Vergiftung' eines Bezeichners bedeutet, dass jeder Verweis auf den Bezeichner nach der 'Vergiftung' ein harter Compilerfehler ist. Diese Technik kann zum Beispiel für harte Verwerfungen verwendet werden (Funktion ist veraltet, verwenden Sie sie niemals!).

Im GCC gab es traditionell ein Pragma dafür : #pragma GCC poison.

SergeyA
quelle
1
Ja, aber nicht ganz in dem Sinne, wie es in diesem Vortrag verwendet wurde.
TC
@TC, ok, ich sollte es mir wahrscheinlich ansehen, bevor ich antworte :)
SergeyA