هل لديك سؤال لا يتطلب منك فتح موضوع؟ انضم إلى قناة جيتر.
إذا كنت تستخدم uvw
وتريد أن تقول شكرًا أو تدعم المشروع، فيرجى التفكير في أن تصبح راعيًا .
يمكنك مساعدتي في إحداث الفرق. شكرا جزيلا لأولئك الذين دعموني وما زالوا يدعمونني اليوم.
بدأ uvw
كغلاف صغير الحجم وسهل الاستخدام لـ libuv
مكتوب بلغة C++ الحديثة، وهو عبارة عن رأسية فقط، وقائم على الأحداث.
والآن أصبح متاحًا أخيرًا أيضًا كمكتبة ثابتة قابلة للتجميع.
الفكرة الأساسية هي تغليف واجهة C-ish الخاصة بـ libuv
خلف واجهة برمجة تطبيقات C++ الرائعة.
لاحظ أن uvw
يظل متوافقًا مع واجهة برمجة التطبيقات الخاصة بـ libuv
ولا يضيف أي شيء إلى واجهته. لنفس الأسباب، يجب على مستخدمي المكتبة اتباع نفس القواعد المستخدمة مع libuv
.
على سبيل المثال، يجب تهيئة المقبض قبل أي عملية أخرى وإغلاقه بمجرد عدم استخدامه.
#تتضمن <uvw.hpp>#تتضمن <الذاكرة>استماع باطل(uvw::loop &loop) { std::shared_ptr<uvw::tcp_handle> tcp = حلقة.resource<uvw::tcp_handle>(); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); client->on<uvw::إغلاق_event>([ptr = srv.shared_from_this()](const uvw::إغلاق_event &, uvw::tcp_handle &) { ptr->إغلاق(); }); Client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.إغلاق(); }); srv.accept(*client); العميل->قراءة(); }); tcp->bind("127.0.0.1", 4242); tcp->listen(); }void conn(uvw::loop &loop) {auto tcp = loop.resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* التعامل مع الأخطاء */ }); tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b' ، 'ج' }); tcp.write(std::move(dataWrite), 2); tcp.Close(); }); tcp->connect(std::string{"127.0.0.1"}, 4242); }int main() {حلقة تلقائية = uvw::loop::get_default();listen(*loop);conn(*loop); حلقة->تشغيل(); }
السبب الرئيسي وراء كتابة uvw
هو عدم وجود غلاف libuv
صالح في لغة C++. هذا كل شيء.
لتتمكن من استخدام uvw
، يجب على المستخدمين توفير الأدوات التالية على مستوى النظام:
مترجم كامل المواصفات يدعم C++ 17 على الأقل.
libuv
(أي إصدار يعتمد على علامة uvw
المستخدمة)
إذا كنت تستخدم meson
، فسيتم تنزيل libuv لك
المتطلبات أدناه إلزامية لتجميع الاختبارات واستخراج الوثائق:
الإصدار 3.13 من CMake أو الأحدث.
دوكسيجين الإصدار 1.8 أو الأحدث.
لاحظ أن libuv
جزء من تبعيات المشروع وقد يتم استنساخه بواسطة CMake
في بعض الحالات (انظر أدناه لمزيد من التفاصيل).
ولهذا السبب، لا يتعين على المستخدمين تثبيته لتشغيل الاختبارات أو عند تجميع مكتبات uvw
من خلال CMake
.
يمكنك استخدام uvw
مع meson ببساطة عن طريق إضافته إلى دليل subprojects
في مشروعك.
لتجميع uvw
من المصدر دون استخدامه كمشروع فرعي، في دليل مصدر uvw
، قم بتشغيل:
$ meson setup build
إذا كنت تريد مكتبة ثابتة، أضف --default-library=static
$ cd build
$ meson compile
uvw
هي مكتبة ذات الوضع المزدوج. يمكن استخدامه في شكل الرأس فقط أو كمكتبة ثابتة مجمعة.
تصف الأقسام التالية ما يجب فعله في كلتا الحالتين لتشغيل uvw
في مشروعك الخاص.
لاستخدام uvw
كمكتبة للرأس فقط، كل ما هو مطلوب هو تضمين رأس uvw.hpp
أو أحد ملفات uvw/*.hpp
الأخرى.
إنها مسألة إضافة السطر التالي في أعلى الملف:
#تشمل <uvw.hpp>
ثم قم بتمرير الوسيطة -I
المناسبة إلى المترجم لإضافة دليل src
إلى مسارات التضمين.
لاحظ أنه يتعين على المستخدمين إعداد أدلة التضمين ومسارات بحث المكتبات بشكل صحيح لـ libuv
في هذه الحالة.
عند استخدامه من خلال CMake
، يتم تصدير هدف uvw::uvw
للراحة.
لاستخدام uvw
كمكتبة مجمعة، قم بتعيين خيارات UVW_BUILD_LIBS
في cmake قبل تضمين المشروع.
يؤدي هذا الخيار إلى إنشاء أهداف تسمى uvw::uvw-static
. يتم أيضًا تجميع الإصدار المطابق من libuv
وتصديره كـ uv::uv-static
للراحة.
في حالة عدم استخدام CMake
أو عدم رغبته في استخدامه، فلا يزال بإمكانك تجميع كافة ملفات .cpp
وتضمين جميع ملفات .h
لإنجاز المهمة. في هذه الحالة، يُطلب من المستخدمين إعداد أدلة التضمين ومسارات بحث المكتبات بشكل صحيح لـ libuv
.
بدءًا من العلامة v1.12.0 من libuv
، يتبع uvw
نظام الإصدار الدلالي.
المشكلة هي أن أي إصدار من uvw
يتطلب أيضًا تتبع إصدار libuv
الذي يرتبط به بشكل صريح.
وبسبب ذلك، سيتم إلحاق الأخير بإصدار uvw
. كمثال:
vU.V.W_libuv-vX.Y
ويطبق على وجه الخصوص ما يلي:
UVW هي إصدارات رئيسية وثانوية وتصحيحية من uvw
.
XY هو إصدار libuv
الذي يجب الإشارة إليه (حيث يكون أي إصدار تصحيح صالحًا).
بمعنى آخر، ستبدو العلامات هكذا من الآن فصاعدًا:
v1.0.0_libuv-v1.12
سيكون الفرع master
لـ uvw
بمثابة فرع قيد التنفيذ يتبع الفرع v1.x من libuv
(على الأقل طالما ظل الفرع الرئيسي ).
يعتمد التوثيق على doxygen
. لبنائه:
$ cd build
$ cmake ..
$ make docs
سيتم إنشاء مرجع واجهة برمجة التطبيقات (API) بتنسيق HTML داخل الدليل build/docs/html
.
للتنقل عبر متصفحك المفضل:
$ cd build
$ your_favorite_browser docs/html/index.html
نفس الإصدار متاح أيضًا عبر الإنترنت لأحدث إصدار، وهو آخر علامة ثابتة.
الوثائق مستوحاة في الغالب من وثائق libuv API الرسمية لأسباب واضحة.
لتجميع الاختبارات وتشغيلها، يتطلب uvw
استخدام libuv
و googletest
.
سيقوم CMake
بتنزيل المكتبتين وتجميعهما قبل تجميع أي شيء آخر.
لبناء الاختبارات:
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
احذف -R uvw
إذا كنت تريد أيضًا اختبار libuv
والتبعيات الأخرى.
هناك قاعدة واحدة فقط عند استخدام uvw
: قم دائمًا بتهيئة الموارد وإنهائها.
تنتمي الموارد بشكل أساسي إلى عائلتين: المقابض والطلبات .
تمثل المقابض كائنات طويلة العمر قادرة على تنفيذ عمليات معينة أثناء نشاطها.
تمثل الطلبات (عادةً) عمليات قصيرة العمر يتم إجراؤها إما عبر معالج أو بشكل مستقل.
ستشرح الأقسام التالية باختصار ما يعنيه تهيئة هذه الأنواع من الموارد وإنهائها.
لمزيد من التفاصيل، يرجى الرجوع إلى الوثائق عبر الإنترنت.
عادةً ما يتم إجراء التهيئة أسفل الغطاء ويمكن حتى تمريرها، بقدر ما يتم إنشاء المقابض باستخدام وظيفة عضو loop::resource
.
على الجانب الآخر، تحافظ المقابض على نفسها حتى يتم إغلاقها بشكل صريح. ولهذا السبب، سيزداد استخدام الذاكرة إذا نسي المستخدمون ببساطة أمر المقبض.
ولذلك فإن القاعدة سرعان ما تصبح قريبة من مقابضك دائمًا . الأمر بسيط مثل استدعاء وظيفة العضو close
عليهم.
عادة، لا يلزم تهيئة كائن الطلب. على أية حال، الطريقة الموصى بها لإنشاء طلب هي من خلال وظيفة عضو loop::resource
.
ستبقى الطلبات على قيد الحياة طالما أنها مرتبطة بالأنشطة الأساسية غير المكتملة. وهذا يعني أنه لا يتعين على المستخدمين تجاهل الطلب بشكل صريح.
ولذلك فإن القاعدة سرعان ما تصبح لا تتردد في تقديم طلب ونسيانه . الأمر بسيط مثل استدعاء وظيفة العضو عليهم.
أول شيء يجب فعله لاستخدام uvw
هو إنشاء حلقة. في حالة ما إذا كان الخيار الافتراضي كافيًا، فمن السهل القيام بذلك:
حلقة تلقائية = uvw::loop::get_default();
لاحظ أن كائنات الحلقة لا تتطلب أن يتم إغلاقها بشكل صريح، حتى لو كانت توفر وظيفة العضو close
في حالة رغبة المستخدم في القيام بذلك.
يمكن بدء الحلقات باستخدام وظيفة عضو run
. النداءتان أدناه متكافئتان:
حلقة->تشغيل(); حلقة->run(uvw::loop::run_mode::DEFAULT);
الأوضاع المتاحة هي: DEFAULT
، ONCE
، NOWAIT
. يرجى الرجوع إلى وثائق libuv
لمزيد من التفاصيل.
من أجل إنشاء مورد وربطه بالحلقة المحددة، فقط قم بما يلي:
auto tcp = حلقة->resource<uvw::tcp_handle>();
يقوم السطر أعلاه بإنشاء مقبض TCP وتهيئته، ثم يتم إرجاع مؤشر مشترك لذلك المورد.
يجب على المستخدمين التحقق مما إذا تم تهيئة المؤشرات بشكل صحيح: في حالة وجود أخطاء، لن يتم ذلك.
من الممكن أيضًا إنشاء موارد غير مهيأة للبدء لاحقًا على النحو التالي:
auto tcp = حلقة->unitialized_resource<uvw::tcp_handle>(); tcp->init();
تقبل جميع الموارد أيضًا بيانات المستخدم العشوائية التي لن يتم المساس بها بأي حال من الأحوال.
يمكن للمستخدمين ضبطها والحصول عليها من خلال وظيفة عضو data
كما يلي:
الموارد->البيانات(std::make_shared<int>(42)); std::shared_ptr<void> data = Resources->data();
تتوقع الموارد std::shared_pointer<void>
وتعيده، لذلك نرحب بأي نوع من البيانات.
يمكن للمستخدمين تحديد نوع آخر غير void
بشكل صريح عند استدعاء وظيفة عضو data
:
std::shared_ptr<int> data = Resources->data<int>();
تذكر من القسم السابق أن المقبض سيبقى على قيد الحياة حتى يتم استدعاء وظيفة العضو close
عليه.
لمعرفة ما هي المقابض التي لا تزال على قيد الحياة ومرتبطة بحلقة معينة، توجد وظيفة عضو walk
. تقوم بإرجاع المقابض بأنواعها. لذلك، يوصى باستخدام overloaded
لتتمكن من اعتراض جميع أنواع الاهتمام:
Handle.parent().walk(uvw::overloaded{ [](uvw::timer_handle &h){ /* رمز تطبيق المؤقتات هنا */ }, [](تلقائي &&){ /* تجاهل كافة الأنواع الأخرى */ } });
يمكن أيضًا استخدام هذه الوظيفة لنهج عام تمامًا. على سبيل المثال، يمكن إغلاق جميع المقابض المعلقة بسهولة كما يلي:
حلقة->walk([](auto &&h){ h. Close(); });
ليست هناك حاجة لتتبعهم.
تقدم uvw
نهجًا قائمًا على الأحداث حيث تكون الموارد عبارة عن بواعث أحداث صغيرة يرتبط بها المستمعون.
إن ربط المستمعين بالموارد هو الطريقة الموصى بها لتلقي إشعارات حول عملياتهم.
المستمعون عبارة عن كائنات قابلة للاستدعاء من النوع void(event_type &, resource_type &)
حيث:
event_type
هو نوع الحدث الذي تم تصميمه من أجله.
resource_type
هو نوع المورد الذي أنشأ الحدث.
وهذا يعني أن أنواع الوظائف التالية كلها صالحة:
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
يرجى ملاحظة أنه ليست هناك حاجة للاحتفاظ بالمراجع إلى الموارد، حيث إنها تعتبر نفسها وسيطة كلما تم نشر حدث ما.
وظيفة on
member هي الطريقة التي يمكن اتباعها لتسجيل المستمعين الدائمين:
الموارد.على<event_type>(المستمع)
لمعرفة ما إذا كان هناك مستمع لنوع معين، يقدم الفصل قالب has
. وبالمثل، يتم استخدام قالب وظيفة reset
لإعادة ضبط المستمعين وبالتالي فصلهم، إن وجد. يوجد أيضًا إصدار غير قالب reset
لمسح الباعث ككل.
تصدر جميع الموارد تقريبًا error_event
في حالة حدوث أخطاء.
جميع الأحداث الأخرى خاصة بالمورد المحدد وموثقة في مرجع واجهة برمجة التطبيقات.
يوضح الكود أدناه كيفية إنشاء خادم TCP بسيط باستخدام uvw
:
حلقة تلقائية = uvw::loop::get_default();auto tcp = حلقة->resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* حدث خطأ ما */ }); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); Client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.إغلاق(); }); Client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* البيانات المستلمة */ }); srv.accept(*client); العميل->قراءة(); }); tcp->bind("127.0.0.1", 4242); tcp->listen();
لاحظ أيضًا أن uvw::tcp_handle
يدعم بالفعل IPv6 الجاهز.
مرجع واجهة برمجة التطبيقات (API) هو الوثائق الموصى بها للحصول على مزيد من التفاصيل حول الموارد وطرقها.
في حالة حاجة المستخدمين إلى استخدام وظائف لم يتم تغليفها بعد بواسطة uvw
أو إذا كانوا يريدون الحصول على بنيات البيانات الأساسية كما حددها libuv
لبعض الأسباب الأخرى، فإن جميع الفئات في uvw
تقريبًا تمنح الوصول المباشر إليها.
يرجى ملاحظة أنه لا ينبغي استخدام هذه الوظائف بشكل مباشر إلا إذا كان المستخدمون يعرفون بالضبط ما يفعلونه وما هي المخاطر. يعد الانتقال إلى الوضع الخام أمرًا خطيرًا، ويرجع ذلك أساسًا إلى أن الإدارة مدى الحياة للحلقة أو المقبض أو الطلب يتم التحكم فيها بالكامل بواسطة المكتبة ويمكن أن يؤدي العمل حولها إلى إتلاف الأشياء بسرعة.
ومع ذلك، فإن التحول إلى الخام هو مسألة استخدام وظائف الأعضاء raw
:
حلقة تلقائية = uvw::loop::get_default();auto tcp = حلقة->مورد<uvw::tcp_handle>();uv_loop_t *raw = حلقة->raw();uv_tcp_t *handle = tcp->raw() ;
اتبع الطريق الصحيح على مسؤوليتك الخاصة، ولكن لا تتوقع أي دعم في حالة وجود أخطاء.
هل أنت مهتم بالأدوات والمكتبات الإضافية التي تعتمد على uvw
؟ قد تجد ما يلي مفيدًا إذن:
uvw_net
: مكتبة شبكات بها مجموعة من العملاء (HTTP/Modbus/SunSpec) تتضمن أيضًا تطبيقات الاكتشاف مثل dns-sd/mdns.
لا تتردد في إضافة أداتك إلى القائمة إذا أردت.
إذا كنت ترغب في المساهمة، يرجى إرسال التصحيحات كطلبات سحب ضد مدير الفرع.
تحقق من قائمة المساهمين لمعرفة من شارك حتى الآن.
الكود والوثائق حقوق الطبع والنشر (ج) 2016-2024 ميشيل كايني.
حقوق الطبع والنشر للشعار (ج) 2018-2021 لريتشارد كاسيريس.
تم إصدار الكود والوثائق بموجب ترخيص MIT.
تم إصدار الشعار تحت CC BY-SA 4.0.
إذا كنت ترغب في دعم هذا المشروع، يمكنك أن تقدم لي قهوة اسبريسو.
إذا وجدت أن هذا لا يكفي، فلا تتردد في مساعدتي بالطريقة التي تفضلها.