Apakah Anda ingin menyederhanakan manajemen seumur hidup dan pemeliharaan objek polimorfik di C ++?
Apakah Anda ingin menulis kode polimorfik dalam C ++ semudah dalam bahasa GC seperti Java atau C#, tanpa mengorbankan kinerja?
Sudahkah Anda mencoba pustaka pemrograman polimorfik lainnya di C ++ tetapi menemukan mereka kekurangan?
Jika demikian, perpustakaan ini cocok untuk Anda.
"Proxy" adalah perpustakaan C ++ modern yang membantu Anda menggunakan polimorfisme (cara untuk menggunakan berbagai jenis objek secara bergantian) tanpa perlu warisan.
"Proxy" dibuat oleh insinyur Microsoft dan telah digunakan dalam sistem operasi Windows sejak 2022. Selama bertahun -tahun, menggunakan pewarisan adalah cara utama untuk mencapai polimorfisme di C ++. Namun, bahasa pemrograman baru seperti Rust menawarkan cara yang lebih baik untuk melakukan ini. Kami telah meningkatkan pemahaman kami tentang pemrograman yang berorientasi objek dan memutuskan untuk menggunakan pointer di C ++ sebagai dasar untuk "proxy". Secara khusus, perpustakaan "proxy" dirancang untuk menjadi:
Silakan merujuk ke pertanyaan proxy yang sering diajukan untuk latar belakang lebih lanjut, dan lihat spesifikasi untuk detail teknis lebih lanjut.
"Proxy" adalah perpustakaan C ++ 20 yang header. Untuk menggunakan perpustakaan, pastikan kompiler Anda memenuhi persyaratan minimum dan hanya menyertakan proxy file header.h dalam kode sumber Anda. Atau, Anda dapat menginstal perpustakaan melalui vcpkg atau conan, dengan mencari "proxy" (lihat vcpkg.io dan conan.io).
Mari kita mulai dengan contoh "Hello World" berikut:
# include < iostream >
# include < string >
# include " proxy.h "
struct Streamable : pro::facade_builder
::add_convention<pro::operator_dispatch< " << " , true >, std::ostream&(std::ostream& out) const >
::build {};
int main () {
std::string str = " Hello World " ;
pro::proxy<Streamable> p1 = &str;
std::cout << " p1 = " << *p1 << " n " ; // Prints: "p1 = Hello World"
pro::proxy<Streamable> p2 = std::make_unique< int >( 123 );
std::cout << " p2 = " << *p2 << " n " ; // Prints: "p2 = 123"
pro::proxy<Streamable> p3 = pro::make_proxy<Streamable>( 3.14 );
std::cout << " p3 = " << *p3 << " n " ; // Prints: "p3 = 3.14"
}Berikut adalah penjelasan langkah demi langkah:
#include <iostream> : untuk std::cout .
#include <string> : untuk std::string .
#include "proxy.h" : untuk perpustakaan "proxy". Sebagian besar fasilitas perpustakaan didefinisikan dalam Namespace pro . Jika perpustakaan dikonsumsi melalui vcpkg atau conan, baris ini harus diubah menjadi #include <proxy/proxy.h> .
struct Streamable : pro::facade_builder ... ::build {} : mendefinisikan tipe fasad Streamable . Istilah "fasad", secara resmi didefinisikan sebagai persyaratan Profacade , adalah bagaimana abstraksi runtime model "proxy". Secara khusus,
pro::facade_builder : Memberikan kemampuan untuk membangun jenis fasad pada waktu kompilasi.add_convention : Menambahkan "konvensi panggilan" umum, ditentukan oleh "pengiriman" dan beberapa "kelebihan beban", ke konteks pembuatan.pro::operator_dispatch <"<<", true> : Menentukan pengiriman untuk operator << ekspresi di mana operan utama ( proxy ) berada di sisi kanan (ditentukan oleh parameter template kedua true ). Perhatikan bahwa polimorfisme dalam perpustakaan "proxy" didefinisikan oleh ekspresi daripada fungsi anggota, yang berbeda dari fungsi virtual C ++ atau bahasa OOP lainnya.std::ostream&(std::ostream& out) const : tanda tangan konvensi panggilan, mirip dengan std::move_only_function . const menentukan bahwa operan utama adalah const .build : Membangun konteks menjadi jenis fasad. pro::proxy <Streamable> p1 = &str : Membuat objek proxy dari pointer mentah std::string . p1 berperilaku seperti penunjuk mentah, dan tidak memiliki kepemilikan atas std::string . Jika masa pakai str berakhir sebelum p1 , p1 menjadi menggantung.
std::cout << *p1 : Beginilah cara kerjanya. Ini mencetak "Hello World" karena konvensi panggilan didefinisikan dalam fasad Streamable , jadi berfungsi seolah -olah dengan menelepon std::cout << str .
pro::proxy <Streamable> p2 = std::make_unique <int>(123) : Membuat std::unique_ptr <int> dan dikonversi ke proxy . Berbeda dari p1 , p2 memiliki kepemilikan terhadap int yang mendasarinya karena dipakai dari nilai std::unique_ptr , dan akan memanggil destruktor std::unique_ptr ketika p2 dihancurkan, sementara p1 tidak memiliki kepemilikan int yang mendasarinya karena diakhiri dari penunjuk mentah. p1 dan p2 memiliki tipe yang sama pro::proxy<Streamable> , yang berarti Anda dapat memiliki fungsi yang mengembalikan pro::proxy<Streamable> tanpa mengekspos informasi apa pun tentang detail implementasi kepada peneleponnya.
std::cout << *p2 : cetak "123" tanpa kejutan.
pro::proxy <Streamable> p3 = pro::make_proxy <Streamable>(3.14) : Membuat proxy dari double tanpa menentukan jenis pointer yang mendasarinya. Secara khusus,
p2 , p3 juga memiliki kepemilikan nilai double yang mendasarinya, tetapi dapat secara efektif menghindari alokasi heap.double ) diketahui kecil (pada platform 32- atau 64-bit utama), pro::make_proxy menyadari fakta pada waktu kompilasi, dan kembali ke pro::make_proxy_inplace , yang menjamin tidak ada alokasi heap.std::function dan pembungkus polimorfik lainnya dalam standar. std::cout << *p3 : cetak "3.14" tanpa kejutan.
Saat pengembalian main , p2 dan p3 akan menghancurkan benda -benda yang mendasarinya, sementara p1 tidak melakukan apa pun karena memegang pointer mentah yang tidak memiliki kepemilikan dari std::string yang mendasarinya.
Selain ekspresi operator yang ditunjukkan dalam contoh sebelumnya, perpustakaan mendukung hampir semua bentuk ekspresi dalam C ++ dan dapat membuatnya polimorfik. Secara khusus,
PRO_DEF_MEM_DISPATCH : Menentukan jenis pengiriman untuk ekspresi panggilan fungsi anggota.PRO_DEF_FREE_DISPATCH : Menentukan jenis pengiriman untuk ekspresi panggilan fungsi gratis.pro::operator_dispatch : Jenis pengiriman untuk ekspresi operator.pro::conversion_dispatch : Jenis Pengiriman untuk Ekspresi Konversi.Perhatikan bahwa beberapa fasilitas disediakan sebagai makro, karena templat C ++ saat ini tidak mendukung menghasilkan fungsi dengan nama sewenang -wenang. Berikut adalah contoh lain yang membuat ekspresi panggilan fungsi anggota polimorfik:
# include < iostream >
# include < sstream >
# include " proxy.h "
PRO_DEF_MEM_DISPATCH (MemDraw, Draw);
PRO_DEF_MEM_DISPATCH (MemArea, Area);
struct Drawable : pro::facade_builder
::add_convention<MemDraw, void (std::ostream& output)>
::add_convention<MemArea, double () noexcept >
::support_copy<pro::constraint_level::nontrivial>
::build {};
class Rectangle {
public:
Rectangle ( double width, double height) : width_(width), height_(height) {}
Rectangle ( const Rectangle&) = default ;
void Draw (std::ostream& out) const {
out << " {Rectangle: width = " << width_ << " , height = " << height_ << " } " ;
}
double Area () const noexcept { return width_ * height_; }
private:
double width_;
double height_;
};
std::string PrintDrawableToString (pro::proxy<Drawable> p) {
std::stringstream result;
result << " entity = " ;
p-> Draw (result);
result << " , area = " << p-> Area ();
return std::move (result). str ();
}
int main () {
pro::proxy<Drawable> p = pro::make_proxy<Drawable, Rectangle>( 3 , 5 );
std::string str = PrintDrawableToString (p);
std::cout << str << " n " ; // Prints: "entity = {Rectangle: width = 3, height = 5}, area = 15"
}Berikut adalah penjelasan langkah demi langkah:
#include <iostream> : untuk std::cout .#include <sstream> : untuk std::stringstream .#include "proxy.h" : untuk perpustakaan "proxy".PRO_DEF_MEM_DISPATCH (MemDraw, Draw) : Menentukan tipe pengiriman MemDraw untuk ekspresi Draw fungsi anggota.PRO_DEF_MEM_DISPATCH (MemArea, Area) : Menentukan tipe pengiriman MemArea untuk ekspresi Area fungsi anggota panggilan.struct Drawable : pro::facade_builder ... ::build {} : mendefinisikan tipe facade Drawable . Secara khusus,add_convention : Menambahkan konvensi panggilan ke konteks build.support_copy < pro::constraint_level ::nontrivial> : Menentukan jenis pointer yang mendasarinya harus disalin, yang juga membuat jenis proxy yang dihasilkan dapat disalin.class Rectangle : Implementasi Drawable .PrintDrawableToString : Mengubah yang Drawable menjadi std::string . Perhatikan bahwa ini adalah fungsi daripada templat fungsi, yang berarti dapat menghasilkan ABI dalam sistem build yang lebih besar.pro::proxy<Drawable> p = pro::make_proxy<Drawable, Rectangle>(3, 5) : Membuat objek proxy<Drawable> yang berisi Rectangle .std::string str = PrintDrawableToString(p) : mengubah p menjadi std::string , secara implisit membuat salinan p .std::cout << str : mencetak string.Perpustakaan "proxy" adalah solusi mandiri untuk polimorfisme runtime di C ++. Ada banyak kemampuan lain yang didokumentasikan dalam spesifikasi. Selain fitur yang disebutkan di atas, berikut adalah daftar fitur paling populer yang dikuratori berdasarkan umpan balik pengguna:
facade_builder::add_convention lebih kuat dari yang ditunjukkan di atas. Ini dapat mengambil sejumlah jenis kelebihan beban (secara formal, jenis apa pun yang memenuhi persyaratan prooverload ) dan melakukan resolusi kelebihan standar saat memohon proxy .facade_builder::add_facade memungkinkan komposisi fleksibel dari abstraksi yang berbeda.PRO_DEF_WEAK_DISPATCH dari jenis pengiriman yang ada dan implementasi default.allocate_proxy dapat membuat proxy dari nilai dengan alokasi khusus apa pun. Dalam C ++ 11, std::function dan std::packaged_task memiliki konstruktor yang menerima alokasi khusus untuk penyetelan kinerja, tetapi ini dihapus dalam C ++ 17 karena "semantik tidak jelas, dan ada masalah teknis dengan menyimpan alokasi dalam konteks yang diturunkan jenis dan kemudian memulihkan alokator itu untuk alokasi apa pun yang dibutuhkan selama salinannya. Masalah -masalah ini tidak berlaku untuk allocate_proxy .facade_builder memberikan dukungan penuh untuk konfigurasi kendala, termasuk tata letak memori (dengan restrict_layout ), copyability (oleh support_copy ), relocatability (oleh support_relocation ), dan kerusakan (oleh support_destruction ).proxy mendukung refleksi waktu kompilasi berbasis tipe untuk kueri runtime. Silakan merujuk ke facade_builder::add_reflection dan function template proxy_reflect untuk lebih detail. | Keluarga | Versi minimum | Bendera yang diperlukan |
|---|---|---|
| GCC | 13.1 | -std = C ++ 20 |
| Dentang | 15.0.0 | -std = C ++ 20 |
| MSVC | 19.31 | /STD: C ++ 20 |
| NVIDIA HPC | 24.1 | -std = C ++ 20 |
git clone https://github.com/microsoft/proxy.git
cd proxy
cmake -B build
cmake --build build -j
ctest --test-dir build -j
Proyek ini menyambut kontribusi dan saran. Sebagian besar kontribusi mengharuskan Anda untuk menyetujui perjanjian lisensi kontributor (CLA) yang menyatakan bahwa Anda memiliki hak untuk, dan benar -benar melakukannya, beri kami hak untuk menggunakan kontribusi Anda. Untuk detailnya, kunjungi https://cla.opensource.microsoft.com.
Saat Anda mengirimkan permintaan tarik, bot CLA akan secara otomatis menentukan apakah Anda perlu memberikan CLA dan menghiasi PR secara tepat (misalnya, pemeriksaan status, komentar). Cukup ikuti instruksi yang disediakan oleh bot. Anda hanya perlu melakukan ini sekali di semua repo menggunakan CLA kami.
Proyek ini telah mengadopsi kode perilaku open source Microsoft. Untuk informasi lebih lanjut, lihat FAQ Kode Perilaku atau hubungi [email protected] dengan pertanyaan atau komentar tambahan.
Proyek ini dapat berisi merek dagang atau logo untuk proyek, produk, atau layanan. Penggunaan resmi merek dagang atau logo Microsoft tunduk dan harus mengikuti pedoman merek dagang & merek Microsoft. Penggunaan merek dagang atau logo Microsoft dalam versi yang dimodifikasi dari proyek ini tidak boleh menyebabkan kebingungan atau menyiratkan sponsor Microsoft. Setiap penggunaan merek dagang atau logo pihak ketiga tunduk pada kebijakan pihak ketiga tersebut.