Apakah Anda memiliki pertanyaan yang tidak mengharuskan Anda membuka masalah? Bergabunglah dengan saluran gitter.
Jika Anda menggunakan uvw
dan ingin mengucapkan terima kasih atau mendukung proyek ini, mohon pertimbangkan untuk menjadi sponsor .
Anda dapat membantu saya membuat perbedaan. Terima kasih banyak kepada mereka yang mendukung saya dan masih mendukung saya hingga saat ini.
uvw
dimulai sebagai pembungkus khusus header, berbasis peristiwa, kecil dan mudah digunakan untuk libuv
yang ditulis dalam C++ modern.
Sekarang akhirnya tersedia juga sebagai perpustakaan statis yang dapat dikompilasi.
Ide dasarnya adalah untuk membungkus antarmuka C-ish dari libuv
di belakang C++ API yang anggun.
Perhatikan bahwa uvw
tetap setia pada API libuv
dan tidak menambahkan apa pun ke antarmukanya. Untuk alasan yang sama, pengguna perpustakaan harus mengikuti aturan yang sama yang digunakan dengan libuv
.
Sebagai contoh, sebuah pegangan harus diinisialisasi sebelum operasi lainnya dan ditutup setelah tidak lagi digunakan.
#include <uvw.hpp>#include <memory>void mendengarkan(uvw::loop &loop) { std::shared_ptr<uvw::tcp_handle> tcp = loop.resource<uvw::tcp_handle>(); tcp->aktif<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> klien = srv.parent().resource<uvw::tcp_handle>(); klien->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); }); klien->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); srv.accept(*klien); klien->baca(); }); tcp->mengikat("127.0.0.1", 4242); tcp->mendengarkan(); }void conn(uvw::loop &loop) {auto tcp = loop.resource<uvw::tcp_handle>(); tcp->aktif<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* menangani kesalahan */ }); tcp->aktif<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(char baru[2]{ 'b' , 'C' }); tcp.write(std::move(dataWrite), 2); tcp.close(); }); tcp->koneksi(std::string{"127.0.0.1"}, 4242); }int main() {auto loop = uvw::loop::get_default();listen(*loop);sambungan(*loop); loop->jalankan(); }
Alasan utama mengapa uvw
ditulis adalah kenyataan bahwa tidak ada pembungkus libuv
yang valid di C++. Itu saja.
Untuk dapat menggunakan uvw
, pengguna harus menyediakan alat seluruh sistem berikut:
Kompiler berfitur lengkap yang mendukung setidaknya C++17.
libuv
(versi mana tergantung pada tag uvw
yang digunakan)
Jika Anda menggunakan meson
, libuv akan diunduh untuk Anda
Persyaratan di bawah ini wajib untuk mengkompilasi pengujian dan mengekstrak dokumentasi:
CMake versi 3.13 atau lebih baru.
Doxygen versi 1.8 atau lebih baru.
Perhatikan bahwa libuv
adalah bagian dari dependensi proyek dan mungkin dikloning oleh CMake
dalam beberapa kasus (lihat di bawah untuk rincian lebih lanjut).
Oleh karena itu, pengguna tidak perlu menginstalnya untuk menjalankan pengujian atau saat perpustakaan uvw
dikompilasi melalui CMake
.
Anda dapat menggunakan uvw
dengan meson hanya dengan menambahkannya ke direktori subprojects
di proyek Anda.
Untuk mengkompilasi uvw
dari sumber tanpa menggunakannya sebagai subproyek, di direktori sumber uvw
, jalankan:
$ meson setup build
Jika Anda menginginkan perpustakaan statis, tambahkan --default-library=static
$ cd build
$ meson compile
uvw
adalah perpustakaan mode ganda. Ini dapat digunakan dalam bentuk header saja atau sebagai perpustakaan statis yang dikompilasi.
Bagian berikut menjelaskan apa yang harus dilakukan dalam kedua kasus tersebut untuk mengaktifkan dan menjalankan uvw
di proyek Anda sendiri.
Untuk menggunakan uvw
sebagai pustaka header saja, yang diperlukan hanyalah menyertakan header uvw.hpp
atau salah satu file uvw/*.hpp
lainnya.
Ini masalah menambahkan baris berikut di bagian atas file:
#termasuk <uvw.hpp>
Kemudian berikan argumen -I
yang tepat ke kompiler untuk menambahkan direktori src
ke jalur penyertaan.
Perhatikan bahwa pengguna diharuskan untuk mengatur dengan benar jalur pencarian direktori dan perpustakaan untuk libuv
dalam kasus ini.
Saat digunakan melalui CMake
, target uvw::uvw
diekspor demi kenyamanan.
Untuk menggunakan uvw
sebagai perpustakaan terkompilasi, atur opsi UVW_BUILD_LIBS
di cmake sebelum menyertakan proyek.
Opsi ini memicu pembuatan target bernama uvw::uvw-static
. Versi libuv
yang cocok juga dikompilasi dan diekspor sebagai uv::uv-static
untuk kenyamanan.
Jika Anda tidak menggunakan atau tidak ingin menggunakan CMake
, Anda masih dapat mengkompilasi semua file .cpp
dan menyertakan semua file .h
untuk menyelesaikan pekerjaan. Dalam hal ini, pengguna diharuskan untuk mengatur dengan benar jalur pencarian direktori dan perpustakaan yang disertakan untuk libuv
.
Dimulai dengan tag v1.12.0 dari libuv
, uvw
mengikuti skema pembuatan versi semantik.
Masalahnya adalah versi uvw
apa pun juga perlu melacak secara eksplisit versi libuv
yang terikat dengannya.
Oleh karena itu, versi terakhir akan ditambahkan ke versi uvw
. Sebagai contoh:
vU.V.W_libuv-vX.Y
Secara khusus, hal berikut ini berlaku:
UVW adalah versi mayor, minor, dan patch dari uvw
.
XY adalah versi libuv
yang menjadi acuan (di mana versi patch apa pun valid).
Dengan kata lain, tag akan terlihat seperti ini mulai sekarang:
v1.0.0_libuv-v1.12
master
cabang uvw
akan menjadi cabang yang sedang dalam proses yang mengikuti cabang v1.x dari libuv
(setidaknya selama cabang tersebut tetap menjadi cabang masternya ).
Dokumentasinya didasarkan pada doxygen
. Untuk membangunnya:
$ cd build
$ cmake ..
$ make docs
Referensi API akan dibuat dalam format HTML di dalam direktori build/docs/html
.
Untuk menavigasinya dengan browser favorit Anda:
$ cd build
$ your_favorite_browser docs/html/index.html
Versi yang sama juga tersedia online untuk rilis terbaru, yaitu tag stabil terakhir.
Dokumentasi ini sebagian besar terinspirasi oleh dokumentasi resmi libuv API karena alasan yang jelas.
Untuk mengkompilasi dan menjalankan pengujian, uvw
memerlukan libuv
dan googletest
.
CMake
akan mengunduh dan mengkompilasi kedua perpustakaan sebelum mengkompilasi yang lainnya.
Untuk membuat pengujian:
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
Hilangkan -R uvw
jika Anda juga ingin menguji libuv
dan dependensi lainnya.
Hanya ada satu aturan saat menggunakan uvw
: selalu inisialisasi sumber daya dan hentikan.
Sumber daya sebagian besar dimiliki oleh dua keluarga: handles dan queries .
Pegangan mewakili objek berumur panjang yang mampu melakukan operasi tertentu saat aktif.
Permintaan mewakili (biasanya) operasi berumur pendek yang dilakukan melalui pegangan atau mandiri.
Bagian berikut akan menjelaskan secara singkat apa yang dimaksud dengan menginisialisasi dan mengakhiri sumber daya semacam ini.
Untuk lebih jelasnya, silakan merujuk ke dokumentasi online.
Inisialisasi biasanya dilakukan di bawah tenda dan bahkan dapat dilewati, sejauh pegangan dibuat menggunakan fungsi anggota loop::resource
.
Di sisi lain, pegangan tetap hidup sampai ada yang menutupnya secara eksplisit. Oleh karena itu, penggunaan memori akan bertambah jika pengguna melupakan sebuah pegangan.
Oleh karena itu aturannya dengan cepat menjadi selalu tutup pegangan Anda . Ini semudah memanggil fungsi anggota close
pada mereka.
Biasanya menginisialisasi objek permintaan tidak diperlukan. Bagaimanapun, cara yang disarankan untuk membuat permintaan masih melalui fungsi anggota loop::resource
.
Permintaan akan tetap hidup selama mereka terikat pada aktivitas mendasar yang belum selesai. Artinya pengguna tidak perlu membuang permintaan secara eksplisit .
Oleh karena itu aturannya dengan cepat menjadi merasa bebas untuk membuat permintaan dan melupakannya . Ini semudah memanggil fungsi anggota pada mereka.
Hal pertama yang harus dilakukan untuk menggunakan uvw
adalah membuat loop. Jika yang default sudah cukup, caranya semudah melakukan ini:
putaran otomatis = uvw::loop::get_default();
Perhatikan bahwa objek loop tidak perlu ditutup secara eksplisit, meskipun objek tersebut menawarkan fungsi anggota close
jika pengguna ingin melakukannya.
Perulangan dapat dimulai menggunakan fungsi run
member. Dua panggilan di bawah ini setara:
loop->jalankan(); loop->jalankan(uvw::loop::run_mode::DEFAULT);
Mode yang tersedia adalah: DEFAULT
, ONCE
, NOWAIT
. Silakan merujuk ke dokumentasi libuv
untuk rincian lebih lanjut.
Untuk membuat sumber daya dan mengikatnya ke loop tertentu, lakukan saja hal berikut:
auto tcp = loop->sumber daya<uvw::tcp_handle>();
Baris di atas membuat dan menginisialisasi pegangan tcp, lalu penunjuk bersama ke sumber daya tersebut dikembalikan.
Pengguna harus memeriksa apakah pointer telah diinisialisasi dengan benar: jika terjadi kesalahan, hal itu tidak akan terjadi.
Dimungkinkan juga untuk membuat sumber daya yang belum diinisialisasi untuk dijalankan nanti sebagai:
auto tcp = loop->uninitialized_resource<uvw::tcp_handle>(); tcp->init();
Semua sumber daya juga menerima data pengguna sewenang-wenang yang tidak akan disentuh dalam hal apa pun.
Pengguna dapat mengatur dan mendapatkannya melalui fungsi anggota data
sebagai berikut:
sumber daya->data(std::make_shared<int>(42)); std::shared_ptr<void> data = sumber daya->data();
Sumber daya mengharapkan std::shared_pointer<void>
dan mengembalikannya, oleh karena itu data apa pun diperbolehkan.
Pengguna dapat secara eksplisit menentukan tipe selain void
saat memanggil fungsi anggota data
:
std::shared_ptr<int> data = sumber daya->data<int>();
Ingat dari bagian sebelumnya bahwa sebuah pegangan akan tetap hidup sampai seseorang memanggil fungsi anggota close
di atasnya.
Untuk mengetahui pegangan apa saja yang masih hidup dan terikat pada loop tertentu, terdapat fungsi anggota walk
. Ia mengembalikan pegangan dengan tipenya. Oleh karena itu, penggunaan overloaded
disarankan untuk dapat mencegat semua jenis kepentingan:
handle.parent().walk(uvw::overloaded{ [](uvw::timer_handle &h){ /* kode aplikasi untuk pengatur waktu di sini */ }, [](auto &&){ /* abaikan semua tipe lainnya */ } });
Fungsi ini juga dapat digunakan untuk pendekatan yang sepenuhnya umum. Misalnya, semua pegangan yang tertunda dapat ditutup dengan mudah sebagai berikut:
loop->berjalan([](otomatis &&h){ h.close(); });
Tidak perlu melacaknya.
uvw
menawarkan pendekatan berbasis peristiwa di mana sumber daya adalah pemancar peristiwa kecil yang menjadi tempat melekatnya pendengar.
Melampirkan pendengar ke sumber daya adalah cara yang disarankan untuk menerima pemberitahuan tentang operasi mereka.
Pendengar adalah objek yang dapat dipanggil dengan tipe void(event_type &, resource_type &)
, dengan:
event_type
adalah jenis acara yang telah dirancang.
resource_type
adalah tipe sumber daya yang menjadi asal mula peristiwa tersebut.
Artinya semua tipe fungsi berikut ini valid:
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
Harap dicatat bahwa tidak perlu menyimpan referensi ke sumber daya, karena referensi tersebut akan menjadi argumen setiap kali suatu peristiwa dipublikasikan.
Fungsi on
member adalah cara untuk mendaftarkan pendengar yang sudah berjalan lama:
resource.on<event_type>(pendengar)
Untuk mengetahui apakah ada pendengar untuk tipe tertentu, kelas menawarkan templat fungsi has
. Demikian pula, templat fungsi reset
digunakan untuk mengatur ulang dan memutuskan sambungan pendengar, jika ada. Versi reset
non-templat juga ada untuk menghapus emitor secara keseluruhan.
Hampir semua sumber daya mengeluarkan error_event
jika terjadi kesalahan.
Semua peristiwa lainnya bersifat spesifik untuk sumber daya tertentu dan didokumentasikan dalam referensi API.
Kode di bawah ini menunjukkan cara membuat server tcp sederhana menggunakan uvw
:
loop otomatis = uvw::loop::get_default();auto tcp = loop->sumber daya<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* ada yang tidak beres */ }); tcp->aktif<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> klien = srv.parent().resource<uvw::tcp_handle>(); klien->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); klien->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* data diterima */ }); srv.accept(*klien); klien->baca(); }); tcp->mengikat("127.0.0.1", 4242); tcp->mendengarkan();
Perhatikan juga bahwa uvw::tcp_handle
sudah mendukung IPv6 secara langsung.
Referensi API adalah dokumentasi yang direkomendasikan untuk rincian lebih lanjut tentang sumber daya dan metodenya.
Jika pengguna perlu menggunakan fungsionalitas yang belum disertakan oleh uvw
atau jika mereka ingin mendapatkan struktur data dasar seperti yang ditentukan oleh libuv
karena beberapa alasan lain, hampir semua kelas di uvw
memberikan akses langsung ke fungsionalitas tersebut.
Harap diperhatikan bahwa fungsi ini tidak boleh digunakan secara langsung kecuali pengguna mengetahui secara pasti apa yang mereka lakukan dan apa risikonya. Menjadi mentah itu berbahaya, terutama karena manajemen loop, pegangan, atau permintaan seumur hidup sepenuhnya dikontrol oleh perpustakaan dan mengatasinya dapat dengan cepat merusak banyak hal.
Meskipun demikian, menjadi mentah adalah masalah menggunakan fungsi anggota raw
:
loop otomatis = uvw::loop::get_default();auto tcp = loop->sumber daya<uvw::tcp_handle>();uv_loop_t *raw = loop->raw();uv_tcp_t *handle = tcp->raw() ;
Lakukan secara mentah-mentah dengan risiko Anda sendiri, tetapi jangan mengharapkan dukungan apa pun jika terjadi bug.
Tertarik dengan alat dan pustaka tambahan yang dibuat berdasarkan uvw
? Anda mungkin menemukan hal berikut berguna:
uvw_net
: perpustakaan jaringan dengan kumpulan klien (HTTP/Modbus/SunSpec) yang juga mencakup implementasi penemuan seperti dns-sd/mdns.
Jangan ragu untuk menambahkan alat Anda ke daftar jika Anda mau.
Jika Anda ingin berkontribusi, silakan kirimkan patch sebagai permintaan tarik terhadap master cabang.
Periksa daftar kontributor untuk melihat siapa yang telah berpartisipasi sejauh ini.
Kode dan dokumentasi Hak Cipta (c) 2016-2024 Michele Caini.
Logo Hak Cipta (c) 2018-2021 Richard Caseres.
Kode dan dokumentasi dirilis di bawah lisensi MIT.
Logo dirilis di bawah CC BY-SA 4.0.
Jika Anda ingin mendukung proyek ini, Anda dapat menawari saya espresso.
Jika menurut Anda itu tidak cukup, silakan bantu saya sesuai keinginan Anda.