Apa yang dimaksud dengan "meracuni suatu fungsi" di C ++?

96

Di akhir ceramah Scott Schurr "Memperkenalkanconstexpr " di CppCon , dia bertanya "Apakah ada cara untuk meracuni suatu fungsi"? Dia kemudian menjelaskan bahwa ini dapat dilakukan (meskipun dengan cara yang tidak standar) dengan:

  1. Menempatkan a throw dalam suatu constexprfungsi
  2. Menyatakan belum terselesaikan extern const char*
  3. Mereferensikan yang belum terselesaikan externdithrow

Saya merasa bahwa saya sedikit keluar dari kedalaman saya di sini, tetapi saya penasaran:

  • Apa artinya "meracuni suatu fungsi"?
  • Apa signifikansi / kegunaan dari teknik yang dia uraikan?
sudo make install
sumber
1
Belum pernah dengar istilah itu, klarifikasi dengan contoh ringkas ya!
πάντα ῥεῖ
6
@ πάνταῥεῖ, saya baru saja mengklarifikasi. Ini adalah istilah yang 'dikenal luas di kalangan kecil'
SergeyA
4
Dia berbicara tentang memastikan bahwa setiap panggilan ke constexprfungsi tersebut dievaluasi pada waktu kompilasi.
TC
@TC Right - dia menyebutkan bahwa suatu constexprfungsi dapat digunakan baik pada waktu kompilasi atau pada waktu berjalan. Jadi ini adalah cara untuk memaksanya agar tidak dapat digunakan saat dijalankan? Kapan itu berguna?
sudo make install
3
Khususnya di C ++ 11, suatu constexprfungsi sering kali bukan implementasi yang paling efisien karena adanya batasan, jadi orang mungkin tidak ingin dievaluasi pada waktu proses; atau, mungkin itu kasus kesalahan (seperti pada contohnya).
TC

Jawaban:

106

Secara umum ini mengacu pada membuat suatu fungsi tidak dapat digunakan, misalnya jika Anda ingin melarang penggunaan alokasi dinamis dalam suatu program, Anda dapat "meracuni" mallocfungsi tersebut sehingga tidak dapat digunakan.

Dalam video, dia menggunakannya dengan cara yang lebih spesifik, yang jelas jika Anda membaca slide yang ditampilkan ketika dia berbicara tentang meracuni fungsi, yang mengatakan "Cara untuk memaksa waktu kompilasi saja?"

Jadi dia berbicara tentang "meracuni" fungsi untuk membuatnya tidak bisa dipanggil pada saat dijalankan, jadi itu hanya bisa dipanggil dalam ekspresi konstan. Tekniknya adalah memiliki cabang dalam fungsi yang tidak pernah diambil ketika dipanggil dalam konteks waktu kompilasi, dan membuat cabang tersebut berisi sesuatu yang akan menyebabkan kesalahan.

Sebuah throwekspresi diperbolehkan dalam fungsi constexpr, selama itu tidak pernah tercapai selama doa saat kompilasi dari fungsi (karena Anda tidak bisa membuang pengecualian pada saat kompilasi, itu operasi inheren dinamis, seperti mengalokasikan memori). Jadi ekspresi lemparan yang merujuk ke simbol yang tidak ditentukan tidak akan digunakan selama pemanggilan waktu kompilasi (karena itu akan gagal untuk dikompilasi) dan tidak dapat digunakan pada waktu proses, karena simbol yang tidak ditentukan menyebabkan kesalahan penaut.

Karena simbol tak terdefinisi tidak "digunakan-odr" dalam pemanggilan fungsi waktu kompilasi, dalam praktiknya kompilator tidak akan membuat referensi ke simbol, jadi tidak masalah jika tak terdefinisi.

Apakah itu berguna? Dia mendemonstrasikan bagaimana melakukannya, tidak harus mengatakan itu ide yang bagus atau berguna secara luas. Jika Anda memiliki kebutuhan untuk melakukannya karena alasan tertentu maka tekniknya mungkin dapat menyelesaikan masalah Anda. Jika Anda tidak membutuhkannya, Anda tidak perlu mengkhawatirkannya.

Salah satu alasan mengapa ini mungkin berguna adalah ketika versi waktu kompilasi dari beberapa operasi tidak seefisien mungkin. Ada batasan pada jenis ekspresi yang diizinkan dalam fungsi constexpr (terutama di C ++ 11, beberapa batasan telah dihapus di C ++ 14). Jadi, Anda mungkin memiliki dua versi fungsi untuk melakukan penghitungan, yang optimal, tetapi menggunakan ekspresi yang tidak diizinkan dalam fungsi konstekspr, dan yang merupakan fungsi konsteks yang valid, tetapi akan berkinerja buruk jika dipanggil saat run- waktu. Anda bisa meracuni yang sub-optimal untuk memastikannya tidak pernah digunakan untuk panggilan run-time, memastikan versi yang lebih efisien (non-constexpr) digunakan untuk panggilan run-time.

NB Performa fungsi constexpr yang digunakan pada waktu kompilasi tidak terlalu penting, karena ia tidak memiliki run-time overhead. Ini mungkin memperlambat kompilasi Anda dengan membuat kompiler melakukan pekerjaan ekstra, tetapi itu tidak akan menimbulkan biaya kinerja waktu proses.

Jonathan Wakely
sumber
1
Saya memang membaca teks slide, tetapi saya tidak melihat hubungan dengan istilah yang dia gunakan. Sudah jelas sekarang Anda telah menjelaskannya, tetapi saya tidak melihatnya saat itu. Terima kasih banyak atas jawaban yang luar biasa ini - Saya suka situs web ini.
sudo make install
@PravasiMeet, ajukan pertanyaan Anda sendiri, jangan membajak komentar pertanyaan orang lain tentang sesuatu yang berbeda. Solusi sederhana adalah dengan mendefinisikannya sebagai dihapus di setiap unit terjemahan, atau menggantinya dengan definisi Anda sendiri yang mereferensikan simbol yang tidak ditentukan.
Jonathan Wakely
17

'Meracuni' sebuah pengenal berarti bahwa setiap referensi ke pengenal setelah 'keracunan' adalah kesalahan kompiler yang sulit. Teknik ini dapat digunakan, misalnya, untuk deprecation (fungsi IS tidak digunakan lagi, jangan pernah menggunakannya!).

Dalam GCC tradisional ada pragma untuk ini: #pragma GCC poison.

SergeyA
sumber
1
Ya, tapi tidak seperti yang digunakan dalam ceramah itu.
TC
@TC, ok, saya mungkin harus menontonnya sebelum menjawab :)
SergeyA