Ada beberapa masalah dengan refleksi di C ++.
Banyak pekerjaan yang harus ditambahkan, dan komite C ++ cukup konservatif, dan tidak menghabiskan waktu untuk fitur baru yang radikal kecuali mereka yakin itu akan membayar. (Sebuah saran untuk menambahkan sistem modul yang mirip dengan .NET rakitan telah dibuat, dan sementara saya pikir ada konsensus umum yang akan menyenangkan untuk dimiliki, itu bukan prioritas utama mereka saat ini, dan telah didorong kembali sampai setelah C ++ 0x Motivasi untuk fitur ini adalah untuk menyingkirkan #include
sistem, tetapi juga akan mengaktifkan setidaknya beberapa metadata).
Anda tidak membayar apa yang tidak Anda gunakan. Itulah salah satu keharusan filosofi desain dasar yang mendasari C ++. Mengapa kode saya harus membawa metadata jika saya tidak pernah membutuhkannya? Selain itu, penambahan metadata dapat menghambat kompiler untuk mengoptimalkan. Mengapa saya harus membayar biaya itu dalam kode saya jika saya mungkin tidak pernah membutuhkan metadata itu?
Yang membawa kita ke titik besar lain: C ++ membuat sangat sedikit jaminan tentang kode yang dikompilasi. Kompiler diperbolehkan untuk melakukan hampir semua hal yang disukainya, asalkan fungsionalitas yang dihasilkan sesuai dengan yang diharapkan. Misalnya, kelas Anda tidak diharuskan benar - benar
ada di sana . Kompiler dapat mengoptimalkannya, sebariskan semua yang mereka lakukan, dan sering kali hanya melakukannya, karena bahkan kode templat sederhana cenderung membuat beberapa contoh templat. Pustaka standar C ++ bergantung pada optimisasi agresif ini. Functors hanya performant jika overhead instantiating dan destructing objek dapat dioptimalkan.
operator[]
pada vektor hanya sebanding dengan pengindeksan array mentah dalam kinerja karena seluruh operator dapat digarisbawahi dan dengan demikian dihapus seluruhnya dari kode yang dikompilasi. C # dan Java membuat banyak jaminan tentang output dari kompiler. Jika saya mendefinisikan kelas di C #, maka kelas itu akan ada di perakitan yang dihasilkan. Bahkan jika saya tidak pernah menggunakannya. Bahkan jika semua panggilan ke fungsi anggotanya dapat diuraikan. Kelas harus ada di sana, sehingga refleksi dapat menemukannya. Sebagian dari ini diringankan oleh kompilasi C # menjadi bytecode, yang berarti kompiler JIT dapathapus definisi kelas dan fungsi sebaris jika suka, bahkan jika kompiler C # awal tidak bisa. Di C ++, Anda hanya memiliki satu kompiler, dan harus mengeluarkan kode yang efisien. Jika Anda diizinkan untuk memeriksa metadata dari eksekusi C ++, Anda akan berharap untuk melihat setiap kelas yang didefinisikan, yang berarti bahwa kompiler harus melestarikan semua kelas yang ditentukan, bahkan jika mereka tidak diperlukan.
Dan kemudian ada template. Templat di C ++ tidak seperti generik dalam bahasa lain. Setiap instantiasi template membuat
tipe baru . std::vector<int>
adalah kelas yang sepenuhnya terpisah dari
std::vector<float>
. Itu menambahkan hingga banyak jenis berbeda di seluruh program. Apa yang harus dilihat oleh refleksi kita? The Template std::vector
? Tetapi bagaimana bisa, karena itu adalah konstruksi kode sumber, yang tidak memiliki arti pada saat runtime? Itu harus melihat kelas yang terpisah
std::vector<int>
dan
std::vector<float>
. Dan
std::vector<int>::iterator
dan
std::vector<float>::iterator
, sama untukconst_iterator
dan seterusnya. Dan begitu Anda masuk ke metaprogramming template, Anda dengan cepat berakhir instantiating ratusan template, yang semuanya mendapatkan inline dan dihapus lagi oleh kompiler. Mereka tidak memiliki arti, kecuali sebagai bagian dari program metap-waktu kompilasi. Haruskah semua ratusan kelas ini terlihat oleh refleksi? Mereka harus, karena kalau tidak refleksi kita tidak akan berguna, jika itu bahkan tidak menjamin bahwa kelas yang saya definisikan akan benar - benar ada . Dan masalah sampingannya adalah bahwa kelas template tidak ada sampai instantiated. Bayangkan sebuah program yang digunakan std::vector<int>
. Haruskah sistem refleksi kita dapat melihat std::vector<int>::iterator
? Di satu sisi, Anda tentu mengharapkannya. Ini adalah kelas yang penting, dan itu didefinisikan dalam hal std::vector<int>
, yang berlakuada di metadata. Di sisi lain, jika program tidak pernah benar-benar menggunakan templat kelas iterator ini, tipenya tidak akan pernah instantiated, dan kompiler tidak akan membuat kelas di tempat pertama. Dan sudah terlambat untuk membuatnya saat runtime, karena membutuhkan akses ke kode sumber.
- Dan akhirnya, refleksi tidak begitu vital di C ++ seperti di C #. Alasannya lagi, templat metaprogramming. Itu tidak bisa menyelesaikan semuanya, tetapi untuk banyak kasus di mana Anda seharusnya menggunakan refleksi, dimungkinkan untuk menulis sebuah program metap yang melakukan hal yang sama pada waktu kompilasi.
boost::type_traits
adalah contoh sederhana. Anda ingin tahu tentang tipe
T
? Periksa itu type_traits
. Di C #, Anda harus memancing setelah mengetik menggunakan refleksi. Refleksi masih akan berguna untuk beberapa hal (penggunaan utama yang dapat saya lihat, yang tidak dapat dengan mudah diganti oleh metaprogramming, adalah untuk kode serialisasi yang di-autogenerasi), tetapi itu akan membawa beberapa biaya signifikan untuk C ++, dan itu tidak perlu sesering mungkin dalam bahasa lain.
Sunting:
Menanggapi komentar:
cdleary: Ya, simbol debug melakukan sesuatu yang serupa, karena mereka menyimpan metadata tentang tipe yang digunakan dalam executable. Tetapi mereka juga menderita dari masalah yang saya jelaskan. Jika Anda pernah mencoba debug versi rilis, Anda akan tahu apa yang saya maksud. Ada kesenjangan logis besar di mana Anda membuat kelas dalam kode sumber, yang telah diuraikan dalam kode akhir. Jika Anda menggunakan refleksi untuk sesuatu yang berguna, Anda perlu membuatnya lebih dapat diandalkan dan konsisten. Karena itu, tipe akan menghilang dan menghilang hampir setiap kali Anda kompilasi. Anda mengubah sedikit detail kecil, dan kompilator memutuskan untuk mengubah tipe mana yang dimasukkan dan mana yang tidak, sebagai respons. Bagaimana Anda mengekstrak sesuatu yang berguna dari itu, ketika Anda apakah bahkan tidak dijamin bahwa jenis yang paling relevan akan terwakili dalam metadata Anda? Jenis yang Anda cari mungkin ada di versi terakhir, tetapi sekarang sudah tidak ada. Dan besok, seseorang akan memeriksa perubahan kecil yang tidak bersalah ke fungsi kecil yang tidak bersalah, yang membuat jenisnya cukup besar sehingga tidak akan sepenuhnya diuraikan, sehingga akan kembali lagi. Itu masih berguna untuk simbol debug, tetapi tidak lebih dari itu. Aku benci mencoba membuat kode serialisasi untuk kelas berdasarkan ketentuan-ketentuan itu. tapi tidak lebih dari itu. Aku benci mencoba membuat kode serialisasi untuk kelas berdasarkan ketentuan-ketentuan itu. tapi tidak lebih dari itu. Aku benci mencoba membuat kode serialisasi untuk kelas berdasarkan ketentuan-ketentuan itu.
Evan Teran: Tentu saja masalah ini dapat diselesaikan. Tapi itu kembali ke poin saya # 1. Dibutuhkan banyak pekerjaan, dan komite C ++ memiliki banyak hal yang mereka anggap lebih penting. Apakah manfaat mendapatkan beberapa refleksi terbatas (dan itu akan terbatas) di C ++ benar-benar cukup besar untuk membenarkan fokus pada hal itu dengan mengorbankan fitur lain? Apakah benar-benar ada manfaat besar dalam menambahkan fitur bahasa inti yang sudah (sebagian besar) dapat dilakukan melalui perpustakaan dan preprosesor seperti QT? Mungkin, tetapi kebutuhannya jauh kurang mendesak daripada jika perpustakaan seperti itu tidak ada. Untuk saran spesifik Anda, saya yakin tidak mengizinkannya pada template akan membuatnya sama sekali tidak berguna. Anda tidak dapat menggunakan refleksi di perpustakaan standar, misalnya. Refleksi seperti apa yang tidakstd::vector
? Template adalah bagian besar dari C ++. Fitur yang tidak berfungsi pada templat pada dasarnya tidak berguna.
Tapi Anda benar, beberapa bentuk refleksi bisa diterapkan. Tapi itu akan menjadi perubahan besar dalam bahasa. Seperti sekarang, jenis secara eksklusif membangun waktu kompilasi. Mereka ada untuk kepentingan kompiler, dan tidak ada yang lain. Setelah kode telah disusun, ada yang tidak ada kelas. Jika Anda meregangkan diri, Anda bisa berpendapat bahwa fungsi masih ada, tetapi sungguh, semua yang ada hanyalah kumpulan instruksi assembler lompatan, dan banyak tumpukan dorong / pop. Tidak banyak yang bisa dilanjutkan, ketika menambahkan metadata seperti itu.
Tapi seperti yang saya katakan, ada proposal untuk perubahan pada model kompilasi, menambahkan modul mandiri, menyimpan metadata untuk tipe tertentu, memungkinkan modul lain untuk merujuk mereka tanpa harus mengacaukan #include
s. Itu awal yang baik, dan sejujurnya, saya terkejut komite standar tidak hanya membuang proposal karena perubahan yang terlalu besar. Jadi mungkin dalam 5-10 tahun? :)
export
danvector<bool>
.typeinfo
'sname()
fungsi HARUS mengembalikan nama yang diketik oleh programmer dan bukan sesuatu terdefinisi. Dan beri kami pengikat untuk enumerator juga. Ini sebenarnya penting untuk serialisasi / deserialisasi, membantu dalam pembuatan pabrik dll.Refleksi membutuhkan beberapa metadata tentang jenis yang akan disimpan di suatu tempat yang dapat ditanyakan. Karena C ++ mengkompilasi ke kode mesin asli dan mengalami perubahan besar karena optimisasi, tampilan aplikasi tingkat tinggi cukup banyak hilang dalam proses kompilasi, akibatnya, tidak mungkin untuk meminta mereka pada saat run time. Java dan .NET menggunakan representasi tingkat yang sangat tinggi dalam kode biner untuk mesin virtual yang memungkinkan tingkat refleksi ini. Namun, dalam beberapa implementasi C ++, ada sesuatu yang disebut Run Time Type Information (RTTI) yang dapat dianggap versi refleksi yang dipreteli.
sumber
Semua bahasa tidak boleh mencoba untuk memasukkan setiap fitur dari setiap bahasa lainnya.
C ++ pada dasarnya adalah assembler makro yang sangat, sangat canggih. Ini BUKAN (dalam arti tradisional) bahasa tingkat tinggi seperti C #, Java, Objective-C, Smalltalk, dll.
Adalah baik untuk memiliki alat yang berbeda untuk pekerjaan yang berbeda. Jika kita hanya memiliki palu, semua hal akan terlihat seperti paku, dll. Memiliki bahasa skrip berguna untuk beberapa pekerjaan, dan bahasa OO reflektif (Java, Obj-C, C #) berguna untuk kelas pekerjaan lain, dan super bahasa-telanjang-tulang dekat-ke-mesin-efisien berguna untuk kelas pekerjaan lain (C ++, C, Assembler).
C ++ melakukan pekerjaan yang luar biasa untuk memperluas teknologi Assembler ke tingkat manajemen kompleksitas yang luar biasa, dan abstraksi untuk membuat pemrograman lebih besar, tugas yang lebih kompleks sangat mungkin bagi manusia. Tetapi itu tidak selalu merupakan bahasa yang paling cocok untuk mereka yang mendekati masalah mereka dari perspektif tingkat tinggi (Lisp, Smalltalk, Java, C #). Jika Anda membutuhkan bahasa dengan fitur-fitur tersebut untuk mengimplementasikan solusi terbaik untuk masalah Anda, maka terima kasih kepada mereka yang telah membuat bahasa seperti itu untuk kita semua gunakan!
Tetapi C ++ adalah untuk mereka yang, untuk alasan apa pun, perlu memiliki korelasi yang kuat antara kode mereka dan operasi mesin yang mendasarinya. Baik efisiensinya, atau driver perangkat pemrograman, atau interaksi dengan layanan OS tingkat rendah, atau apa pun, C ++ lebih cocok untuk tugas-tugas itu.
C #, Java, Objective-C semuanya membutuhkan sistem runtime yang lebih besar dan lebih kaya untuk mendukung eksekusi mereka. Runtime itu harus dikirimkan ke sistem yang dipermasalahkan - sudah diinstal sebelumnya untuk mendukung pengoperasian perangkat lunak Anda. Dan layer itu harus dipertahankan untuk berbagai sistem target, dikustomisasi oleh BEBERAPA BAHASA LAIN untuk membuatnya bekerja pada platform itu. Dan lapisan tengah itu - lapisan adaptif antara OS host dan kode Anda - runtime, hampir selalu ditulis dalam bahasa seperti C atau C ++ di mana efisiensinya adalah # 1, di mana pemahaman dapat diprediksi interaksi yang tepat antara perangkat lunak dan perangkat keras dapat dengan baik dipahami, dan dimanipulasi untuk mendapatkan hasil maksimal.
Saya suka Smalltalk, Objective-C, dan memiliki sistem runtime yang kaya dengan refleksi, meta-data, pengumpulan sampah, dll. Kode yang luar biasa dapat ditulis untuk memanfaatkan fasilitas ini! Tapi itu hanyalah lapisan yang lebih tinggi pada tumpukan, lapisan yang harus bersandar pada lapisan yang lebih rendah, yang pada akhirnya harus berada di atas OS dan perangkat keras. Dan kita akan selalu membutuhkan bahasa yang paling cocok untuk membangun lapisan itu: C ++ / C / Assembler.
Tambahan: C ++ 11/14 terus memperluas kemampuan C ++ untuk mendukung abstraksi dan sistem tingkat tinggi. Threading, sinkronisasi, model memori yang tepat, definisi mesin abstrak yang lebih tepat memungkinkan pengembang C ++ untuk mencapai banyak abstraksi tingkat tinggi yang beberapa bahasa tingkat tinggi ini hanya digunakan untuk memiliki domain eksklusif, sambil terus menyediakan dekat dengan kinerja logam dan kemampuan prediksi yang sangat baik (yaitu subsistem runtime minimal). Mungkin fasilitas refleksi akan diaktifkan secara selektif dalam revisi C ++ di masa mendatang, bagi mereka yang menginginkannya - atau mungkin perpustakaan akan menyediakan layanan runtime seperti itu (mungkin sekarang ada, atau awal dari salah satu yang mendorong?).
sumber
Jika Anda benar-benar ingin memahami keputusan desain seputar C ++, temukan salinan Manual Referensi C ++ Beranotasi oleh Ellis dan Stroustrup. BUKAN dengan standar terbaru, tetapi melewati standar asli dan menjelaskan cara kerja dan sering, bagaimana mereka mendapatkannya.
sumber
Refleksi untuk bahasa yang memilikinya adalah tentang seberapa banyak kode sumber yang bersedia ditinggalkan oleh kompiler dalam kode objek Anda untuk mengaktifkan refleksi, dan berapa banyak mesin analisis yang tersedia untuk menafsirkan informasi yang direfleksikan itu. Kecuali jika kompilator menyimpan semua kode sumber, refleksi akan terbatas dalam kemampuannya untuk menganalisis fakta yang tersedia tentang kode sumber.
Kompiler C ++ tidak menyimpan apa-apa (well, mengabaikan RTTI), jadi Anda tidak mendapatkan refleksi dalam bahasa tersebut. (Kompiler Java dan C # hanya menyimpan kelas, nama metode dan jenis kembali, sehingga Anda mendapatkan sedikit data refleksi, tetapi Anda tidak dapat memeriksa ekspresi atau struktur program, dan itu berarti bahkan dalam bahasa yang "berkemampuan refleksi" itu informasi yang Anda dapat sangat jarang dan akibatnya Anda benar-benar tidak dapat melakukan banyak analisis).
Tapi Anda bisa keluar dari bahasa dan mendapatkan kemampuan refleksi penuh. Jawaban untuk diskusi stack overflow pada refleksi di C membahas hal ini.
sumber
Refleksi dapat dan telah diimplementasikan dalam c ++ sebelumnya.
Ini bukan fitur c ++ asli karena memiliki biaya yang besar (memori dan kecepatan) yang tidak boleh diatur secara default oleh bahasa - bahasanya berorientasi "kinerja maksimum secara default".
Karena Anda tidak boleh membayar untuk apa yang tidak Anda butuhkan, dan seperti yang Anda katakan pada diri sendiri, itu diperlukan lebih banyak di editor daripada di aplikasi lain, maka itu harus diimplementasikan hanya di mana Anda membutuhkannya, dan tidak "dipaksa" untuk semua kode ( Anda tidak perlu merefleksikan semua data yang akan bekerja dengan Anda di editor atau aplikasi serupa lainnya).
sumber
Alasan C ++ tidak memiliki refleksi adalah karena ini membutuhkan kompiler untuk menambahkan informasi simbol ke file objek, seperti apa yang anggota kelas miliki, informasi tentang anggota, tentang fungsi dan semuanya. Ini pada dasarnya akan membuat menyertakan file tidak berguna, karena informasi yang dikirim oleh deklarasi kemudian akan dibaca dari file objek tersebut (modul kemudian). Dalam C ++, definisi tipe dapat muncul beberapa kali dalam suatu program dengan memasukkan header masing-masing (asalkan semua definisi itu sama), sehingga harus diputuskan di mana harus meletakkan informasi tentang tipe itu, seperti menyebutkan satu komplikasi di sini. Optimasi agresif yang dilakukan oleh kompiler C ++, yang dapat mengoptimalkan lusinan contoh template kelas, adalah titik kuat lainnya. Itu mungkin, tetapi karena C ++ kompatibel dengan C,
sumber
Ada banyak kasus untuk menggunakan refleksi dalam C + + yang tidak dapat ditangani secara memadai menggunakan konstruksi waktu kompilasi seperti meta-pemrograman template.
N3340 mengusulkan pointer kaya sebagai cara untuk memperkenalkan refleksi dalam C ++. Di antara hal-hal lain yang dibahas adalah masalah tidak membayar fitur kecuali Anda menggunakannya.
sumber
Menurut Alistair Cockburn, subtyping tidak dapat dijamin dalam lingkungan yang reflektif .
Refleksi lebih relevan untuk sistem pengetikan laten. Di C ++, Anda tahu jenis apa yang Anda miliki dan Anda tahu apa yang dapat Anda lakukan dengannya.
sumber
Refleksi bisa opsional, seperti arahan preprosesor. Sesuatu seperti
#pragma enable reflection
Dengan begitu kita dapat memiliki yang terbaik dari kedua dunia, tanpa pragma ini perpustakaan akan dibuat tanpa refleksi (tanpa overhead seperti yang dibahas), maka akan tergantung pengembang individu apakah mereka ingin kecepatan atau kemudahan penggunaan.
sumber
Jika C ++ dapat memiliki:
const
pengubahconst
pengubahItu akan cukup untuk membuat perpustakaan yang sangat mudah digunakan pada inti dari pemrosesan data tanpa jenis yang begitu lazim dalam aplikasi web dan database saat ini (semua orme, mekanisme pengiriman pesan, parser xml / json, serialisasi data, dll).
Misalnya, informasi dasar yang didukung oleh
Q_PROPERTY
makro (bagian dari Qt Framework) http://qt.nokia.com/doc/4.5/properties.html diperluas untuk mencakup metode kelas dan e) - akan sangat bermanfaat bagi C ++ dan untuk komunitas perangkat lunak secara umum.Tentu saja refleksi yang saya maksudkan tidak akan mencakup makna semantik atau masalah yang lebih kompleks (seperti komentar nomor baris kode sumber, analisis aliran data, dll) - tetapi saya juga tidak berpikir itu diperlukan untuk menjadi bagian dari standar bahasa.
sumber
beberapa tautan bagus tentang refleksi di C ++ saya baru saja menemukan:
Kertas Kerja Standar C ++: Aspek Refleksi dalam C ++
Contoh sederhana refleksi menggunakan templat
sumber
Refleksi dalam C ++, saya percaya sangat penting jika C ++ akan digunakan sebagai bahasa untuk Akses Database, penanganan sesi Web / http dan pengembangan GUI. Kurangnya refleksi mencegah ORM (seperti Hibernate atau LINQ), parser XML dan JSON yang meng-instancinate class, serialisasi data, dan banyak thigns lainnya (di mana data tanpa tipe awalnya harus digunakan untuk membuat instance kelas).
Pergantian waktu kompilasi yang tersedia untuk pengembang perangkat lunak selama proses pembuatan dapat digunakan untuk menghilangkan kekhawatiran 'Anda membayar untuk apa yang Anda gunakan'.
Saya seorang firmwaredeveloper tidak perlu refleksi untuk membaca data dari port serial - maka denda tidak menggunakan saklar. Tetapi sebagai pengembang basis data yang ingin terus menggunakan C ++ saya secara bertahap dihapus dengan kode yang mengerikan, sulit untuk mempertahankan yang memetakan data antara anggota data dan konstruksi basis data.
Baik serialisasi Boost maupun mekanisme lainnya tidak benar-benar menyelesaikan refleksi - itu harus dilakukan oleh kompiler - dan begitu selesai C ++ akan kembali menjadi sulit di sekolah dan digunakan dalam perangkat lunak yang berurusan dengan pemrosesan data
Bagi saya masalah ini # 1 (dan primitif threading naitive adalah masalah # 2).
sumber
Ini pada dasarnya karena ini adalah "tambahan opsional". Banyak orang memilih C ++ daripada bahasa seperti Java dan C # sehingga mereka memiliki kontrol lebih besar atas keluaran kompiler, mis. Program yang lebih kecil, dan / atau lebih cepat.
Jika Anda memilih untuk menambahkan refleksi ada berbagai solusi yang tersedia .
sumber