У вас есть вопрос , который не требует открытия проблемы? Присоединяйтесь к каналу Gitter.
Если вы используете uvw и хотите сказать спасибо или поддержать проект, рассмотрите возможность стать спонсором .
Вы можете помочь мне изменить ситуацию. Огромное спасибо тем, кто поддерживал меня и поддерживает до сих пор.
uvw начинался как маленькая и простая в использовании оболочка для libuv , основанная на событиях и написанная на современном C++.
Теперь она наконец доступна и в виде компилируемой статической библиотеки.
Основная идея состоит в том, чтобы обернуть C- интерфейс libuv в изящный C++ API.
Обратите внимание, что uvw остается верным API libuv и ничего не добавляет к своему интерфейсу. По тем же причинам пользователи библиотеки должны следовать тем же правилам, которые используются с libuv .
Например, дескриптор должен быть инициализирован перед любой другой операцией и закрыт, когда он больше не используется.
#include <uvw.hpp>#include <memory>void Listen(uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = Loop.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::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); });
client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
srv.accept(*клиент);
клиент->читать();
});
tcp->bind("127.0.0.1", 4242);
TCP->прослушивать();
}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.закрыть();
});
tcp->connect(std::string{"127.0.0.1"}, 4242);
}int main() {auto цикл = uvw::loop::get_default();listen(*loop);conn(*loop);
цикл-> выполнить();
} Основная причина написания uvw заключается в том, что в C++ не существует допустимой оболочки libuv . Вот и все.
Чтобы иметь возможность использовать uvw , пользователи должны предоставить следующие общесистемные инструменты:
Полнофункциональный компилятор, поддерживающий как минимум C++17.
libuv (какая версия зависит от используемого тега uvw )
Если вы используете meson , для вас будет загружена libuv.
Приведенные ниже требования являются обязательными для составления тестов и извлечения документации:
CMake версии 3.13 или более поздней.
Doxygen версии 1.8 или новее.
Обратите внимание, что libuv является частью зависимостей проекта и в некоторых случаях может быть клонирован CMake (более подробную информацию см. ниже).
Благодаря этому пользователям не нужно устанавливать его для запуска тестов или при компиляции uvw -библиотек через CMake .
Вы можете использовать uvw с мезоном, просто добавив его в каталог subprojects вашего проекта.
Чтобы скомпилировать uvw из исходного кода, не используя его в качестве подпроекта, в каталоге исходного кода uvw выполните:
$ meson setup build
Если вам нужна статическая библиотека, добавьте --default-library=static
$ cd build
$ meson compile
uvw — двухрежимная библиотека. Его можно использовать в форме только заголовка или как скомпилированную статическую библиотеку.
В следующих разделах описывается, что делать в обоих случаях, чтобы запустить uvw в вашем собственном проекте.
Чтобы использовать uvw в качестве библиотеки только для заголовков, достаточно включить заголовок uvw.hpp или один из других файлов uvw/*.hpp .
Это вопрос добавления следующей строки в начало файла:
#include <uvw.hpp>
Затем передайте компилятору правильный аргумент -I , чтобы добавить каталог src во включаемые пути.
Обратите внимание, что в этом случае пользователям необходимо правильно настроить пути поиска включаемых каталогов и библиотек для libuv .
При использовании через CMake для удобства цель uvw::uvw экспортируется.
Чтобы использовать uvw в качестве скомпилированной библиотеки, установите параметры UVW_BUILD_LIBS в cmake перед включением проекта.
Эта опция запускает генерацию цели с именем uvw::uvw-static . Соответствующая версия libuv также для удобства компилируется и экспортируется как uv::uv-static .
Если вы не используете или не хотите использовать CMake , вы все равно можете скомпилировать все файлы .cpp и включить все файлы .h , чтобы выполнить работу. В этом случае пользователям необходимо правильно настроить пути поиска включаемых каталогов и библиотек для libuv .
Начиная с тега libuv v1.12.0 , 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
Та же самая версия также доступна в Интернете для последней версии, которая является последней стабильной версией.
Документация в основном основана на официальной документации API libuv по очевидным причинам.
Для компиляции и запуска тестов 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 = цикл->uninitialized_resource<uvw::tcp_handle>(); TCP-> инициализация ();
Все ресурсы также принимают произвольные пользовательские данные, которые ни в коем случае не будут затронуты.
Пользователи могут устанавливать и получать их с помощью функции-члена data следующим образом:
ресурс->данные(std::make_shared<int>(42)); std::shared_ptr<void> данные = ресурс->данные();
Ресурсы ожидают 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){ /* здесь код приложения для таймеров */ },
[](auto &&){ /* игнорировать все остальные типы */ }
});Эту функцию также можно использовать для совершенно общего подхода. Например, все ожидающие дескрипторы можно легко закрыть следующим образом:
цикл->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 — это способ регистрации долго работающих прослушивателей:
ресурс.он<тип_события>(прослушиватель)
Чтобы узнать, существует ли прослушиватель для данного типа, класс предлагает шаблон функции has . Аналогично, шаблон функции reset используется для сброса и, таким образом, отключения прослушивателей, если таковые имеются. Также существует нешаблонная версия reset для очистки эмиттера в целом.
Почти все ресурсы выдают error_event в случае ошибок.
Все остальные события специфичны для данного ресурса и описаны в справочнике API.
Код ниже показывает, как создать простой tcp-сервер с помощью uvw :
автоматический цикл = uvw::loop::get_default();auto tcp = цикл->ресурс<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.close(); });
client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* данные получены */ });
srv.accept(*клиент);
клиент->читать();
});
tcp->bind("127.0.0.1", 4242);
TCP->прослушивать(); Также обратите внимание, что uvw::tcp_handle уже поддерживает IPv6 «из коробки».
Справочник по API — это рекомендуемая документация для получения более подробной информации о ресурсах и их методах.
В случае, если пользователям необходимо использовать функциональные возможности, еще не обернутые uvw , или если они хотят получить базовые структуры данных, определенные libuv по каким-либо другим причинам, почти все классы в uvw предоставляют к ним прямой доступ.
Обратите внимание, что эти функции не следует использовать напрямую, если пользователи точно не знают, что они делают и каковы риски. Работать в сыром виде опасно, главным образом потому, что управление жизненным циклом, дескриптором или запросом полностью контролируется библиотекой, и работа над этим может быстро привести к поломке.
При этом переход к сырому формату — это вопрос использования raw функций-членов:
auto цикл = 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.
Если хотите, добавьте свой инструмент в список.
Если вы хотите внести свой вклад, отправьте исправления в виде запросов на включение к мастеру ветки.
Проверьте список участников, чтобы узнать, кто уже принял участие.
Код и документация Copyright (c) 2016–2024, Мишель Каини.
Авторские права на логотип (c) 2018–2021 Ричард Казерес.
Код и документация выпущены по лицензии MIT.
Логотип выпущен под лицензией CC BY-SA 4.0.
Если вы хотите поддержать этот проект, можете предложить мне эспрессо.
Если вы обнаружите, что этого недостаточно, не стесняйтесь помочь мне так, как вам удобнее.