¿Tiene alguna pregunta que no requiera que abra una incidencia? Únete al canal gitter.
Si utiliza uvw
y quiere agradecer o apoyar el proyecto, considere convertirse en patrocinador .
Puedes ayudarme a marcar la diferencia. Muchas gracias a quienes me apoyaron y todavía me apoyan hoy.
uvw
comenzó como un contenedor pequeño y fácil de usar, basado en eventos, solo de encabezado para libuv
escrito en C++ moderno.
Ahora finalmente está disponible también como biblioteca estática compilable.
La idea básica es envolver la interfaz C-ish de libuv
detrás de una API C++ elegante.
Tenga en cuenta que uvw
se mantiene fiel a la API de libuv
y no agrega nada a su interfaz. Por las mismas razones, los usuarios de la biblioteca deben seguir las mismas reglas que se utilizan con libuv
.
Por ejemplo, un identificador debe inicializarse antes de cualquier otra operación y cerrarse una vez que ya no esté en uso.
#include <uvw.hpp>#include <memoria>void listening(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> cliente = srv.parent().resource<uvw::tcp_handle>(); cliente->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); }); cliente->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); srv.accept(*cliente); cliente->leer(); }); tcp->bind("127.0.0.1", 4242); tcp->escuchar(); }void conn(uvw::loop &loop) {auto tcp = loop.resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* manejar errores */ }); tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b' , 'c' }); tcp.write(std::move(dataWrite), 2); tcp.close(); }); tcp->connect(std::string{"127.0.0.1"}, 4242); }int main() {auto loop = uvw::loop::get_default();listen(*loop);conn(*loop); bucle->ejecutar(); }
La razón principal por la que se escribió uvw
es el hecho de que no existe un contenedor libuv
válido en C++. Eso es todo.
Para poder utilizar uvw
, los usuarios deben proporcionar las siguientes herramientas para todo el sistema:
Un compilador con todas las funciones que admite al menos C++17.
libuv
(cuya versión depende de la etiqueta de uvw
en uso)
Si usas meson
, se descargará libuv por ti.
Los siguientes requisitos son obligatorios para realizar las pruebas y extraer la documentación:
CMake versión 3.13 o posterior.
Doxygen versión 1.8 o posterior.
Tenga en cuenta que libuv
es parte de las dependencias del proyecto y CMake
puede clonarlo en algunos casos (consulte a continuación para obtener más detalles).
Por eso, los usuarios no tienen que instalarlo para ejecutar las pruebas o cuando las bibliotecas uvw
se compilan a través de CMake
.
Puede usar uvw
con meson simplemente agregándolo al directorio subprojects
de su proyecto.
Para compilar uvw
desde el código fuente sin usarlo como subproyecto, en el directorio fuente uvw
, ejecute:
$ meson setup build
Si desea una biblioteca estática, agregue --default-library=static
$ cd build
$ meson compile
uvw
es una biblioteca de modo dual. Se puede utilizar en su forma de solo encabezado o como una biblioteca estática compilada.
Las siguientes secciones describen qué hacer en ambos casos para poner en funcionamiento uvw
en su propio proyecto.
Para usar uvw
como biblioteca de solo encabezados, todo lo que se necesita es incluir el encabezado uvw.hpp
o uno de los otros archivos uvw/*.hpp
.
Es cuestión de agregar la siguiente línea en la parte superior de un archivo:
#incluir <uvw.hpp>
Luego pase el argumento -I
adecuado al compilador para agregar el directorio src
a las rutas de inclusión.
Tenga en cuenta que en este caso los usuarios deben configurar correctamente los directorios de inclusión y las rutas de búsqueda de bibliotecas para libuv
.
Cuando se utiliza a través de CMake
, el destino uvw::uvw
se exporta para mayor comodidad.
Para usar uvw
como biblioteca compilada, configure las opciones UVW_BUILD_LIBS
en cmake antes de incluir el proyecto.
Esta opción desencadena la generación de objetivos denominados uvw::uvw-static
. La versión correspondiente de libuv
también se compila y exporta como uv::uv-static
para mayor comodidad.
En caso de que no utilice o no desee utilizar CMake
, aún puede compilar todos los archivos .cpp
e incluir todos los archivos .h
para realizar el trabajo. En este caso, los usuarios deben configurar correctamente los directorios de inclusión y las rutas de búsqueda de bibliotecas para libuv
.
A partir de la etiqueta v1.12.0 de libuv
, uvw
sigue el esquema de control de versiones semántico.
El problema es que cualquier versión de uvw
también requiere rastrear explícitamente la versión de libuv
a la que está vinculada.
Por eso, este último se agregará a la versión de uvw
. Como ejemplo:
vU.V.W_libuv-vX.Y
En particular, se aplica lo siguiente:
UVW son versiones mayor, menor y de parche de uvw
.
XY es la versión de libuv
a la que hacer referencia (donde cualquier versión de parche es válida).
En otras palabras, las etiquetas tendrán este aspecto a partir de ahora:
v1.0.0_libuv-v1.12
La rama master
de uvw
será una rama de trabajo en progreso que sigue a la rama v1.x de libuv
(al menos mientras siga siendo su rama maestra ).
La documentación se basa en doxygen
. Para construirlo:
$ cd build
$ cmake ..
$ make docs
La referencia de API se creará en formato HTML dentro del directorio build/docs/html
.
Para navegarlo con tu navegador favorito:
$ cd build
$ your_favorite_browser docs/html/index.html
La misma versión también está disponible en línea para la última versión, es decir, la última etiqueta estable.
La documentación está inspirada principalmente en la documentación oficial de la API de libuv por razones obvias.
Para compilar y ejecutar las pruebas, uvw
requiere libuv
y googletest
.
CMake
descargará y compilará ambas bibliotecas antes de compilar cualquier otra cosa.
Para construir las pruebas:
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
Omita -R uvw
si también desea probar libuv
y otras dependencias.
Solo hay una regla al usar uvw
: siempre inicialice los recursos y finalícelos.
Los recursos pertenecen principalmente a dos familias: identificadores y solicitudes .
Los identificadores representan objetos de larga duración capaces de realizar ciertas operaciones mientras están activos.
Las solicitudes representan (normalmente) operaciones de corta duración realizadas a través de un identificador o de forma independiente.
Las siguientes secciones explicarán brevemente lo que significa inicializar y terminar este tipo de recursos.
Para obtener más detalles, consulte la documentación en línea.
La inicialización generalmente se realiza de forma oculta e incluso se puede pasar por alto, en la medida en que los identificadores se creen usando la función miembro loop::resource
.
Por otro lado, las manijas se mantienen vivas hasta que se las cierra explícitamente. Por eso, el uso de la memoria aumentará si los usuarios simplemente se olvidan de un identificador.
Por lo tanto, la regla se convierte rápidamente en cerrar siempre las manijas . Es tan simple como llamarles a la función de miembro close
.
Por lo general, no es necesario inicializar un objeto de solicitud. De todos modos, la forma recomendada de crear una solicitud sigue siendo a través de la función miembro loop::resource
.
Las solicitudes se mantendrán vivas mientras estén vinculadas a actividades subyacentes inacabadas. Esto significa que los usuarios no tienen que descartar una solicitud explícitamente.
Por lo tanto, la regla rápidamente es sentirse libre de hacer una solicitud y olvidarse de ella . Es tan simple como llamarles a una función miembro.
Lo primero que debemos hacer para usar uvw
es crear un bucle. En caso de que el predeterminado sea suficiente, es tan fácil como hacer esto:
bucle automático = uvw::loop::get_default();
Tenga en cuenta que los objetos de bucle no requieren cerrarse explícitamente, incluso si ofrecen la función de miembro close
en caso de que un usuario quiera hacerlo.
Los bucles se pueden iniciar utilizando la función miembro run
. Las dos llamadas siguientes son equivalentes:
bucle->ejecutar(); bucle->ejecutar(uvw::loop::run_mode::DEFAULT);
Los modos disponibles son: DEFAULT
, ONCE
, NOWAIT
. Consulte la documentación de libuv
para obtener más detalles.
Para crear un recurso y vincularlo al bucle dado, simplemente haga lo siguiente:
auto tcp = bucle->recurso<uvw::tcp_handle>();
La línea anterior crea e inicializa un identificador tcp y luego se devuelve un puntero compartido a ese recurso.
Los usuarios deben comprobar si los punteros se han inicializado correctamente: en caso de errores, no lo serán.
También es posible crear recursos no inicializados para iniciarlos más adelante como:
auto tcp = bucle->recurso_uninicializado<uvw::tcp_handle>(); tcp->init();
Todos los recursos también aceptan datos de usuario arbitrarios que no serán manipulados en ningún caso.
Los usuarios pueden configurarlos y obtenerlos a través de la función de miembro data
de la siguiente manera:
recurso->datos(std::make_shared<int>(42)); std::shared_ptr<void> datos = recurso->datos();
Los recursos esperan un std::shared_pointer<void>
y lo devuelven, por lo tanto, cualquier tipo de datos es bienvenido.
Los usuarios pueden especificar explícitamente un tipo distinto de void
al llamar a la función miembro data
:
std::shared_ptr<int> datos = recurso->datos<int>();
Recuerde de la sección anterior que un identificador se mantendrá activo hasta que uno invoque la función de miembro close
en él.
Para saber cuáles son los identificadores que aún están activos y vinculados a un bucle determinado, existe la función miembro walk
. Devuelve identificadores con sus tipos. Por tanto, se recomienda el uso de overloaded
para poder interceptar todo tipo de interés:
handle.parent().walk(uvw::sobrecargado{ [](uvw::timer_handle &h){ /* código de aplicación para temporizadores aquí */ }, [](auto &&){ /* ignorar todos los demás tipos */ } });
Esta función también se puede utilizar para un enfoque completamente genérico. Por ejemplo, todos los identificadores pendientes se pueden cerrar fácilmente de la siguiente manera:
bucle->walk([](auto &&h){ h.close(); });
No es necesario realizar un seguimiento de ellos.
uvw
ofrece un enfoque basado en eventos donde los recursos son pequeños emisores de eventos a los que se adjuntan oyentes.
Adjuntar oyentes a los recursos es la forma recomendada de recibir notificaciones sobre sus operaciones.
Los oyentes son objetos invocables de tipo void(event_type &, resource_type &)
, donde:
event_type
es el tipo de evento para el que han sido diseñados.
resource_type
es el tipo de recurso que ha originado el evento.
Significa que los siguientes tipos de funciones son todos válidos:
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
Tenga en cuenta que no es necesario mantener referencias a los recursos, ya que se hacen pasar por sí mismos como un argumento cada vez que se publica un evento.
La función on
member es el camino a seguir para registrar oyentes de larga duración:
recurso.on<tipo_evento>(oyente)
Para saber si existe un oyente para un tipo determinado, la clase ofrece una plantilla de función has
. De manera similar, la plantilla de función reset
se usa para reiniciar y así desconectar a los oyentes, si los hay. También existe una versión de reset
sin plantilla para borrar un emisor en su totalidad.
Casi todos los recursos emiten error_event
en caso de errores.
Todos los demás eventos son específicos del recurso determinado y están documentados en la referencia de API.
El siguiente código muestra cómo crear un servidor TCP simple usando uvw
:
bucle automático = uvw::loop::get_default();auto tcp = bucle->recurso<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* algo salió mal */ }); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> cliente = srv.parent().resource<uvw::tcp_handle>(); cliente->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); cliente->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* datos recibidos */ }); srv.accept(*cliente); cliente->leer(); }); tcp->bind("127.0.0.1", 4242); tcp->escuchar();
Tenga en cuenta también que uvw::tcp_handle
ya admite IPv6 listo para usar.
La referencia de API es la documentación recomendada para obtener más detalles sobre los recursos y sus métodos.
En caso de que los usuarios necesiten utilizar funcionalidades que aún no están incluidas en uvw
o si desean obtener las estructuras de datos subyacentes definidas por libuv
por otras razones, casi todas las clases en uvw
brindan acceso directo a ellas.
Tenga en cuenta que estas funciones no deben utilizarse directamente a menos que los usuarios sepan exactamente lo que están haciendo y cuáles son los riesgos. Usarlo sin formato es peligroso, principalmente porque la administración de por vida de un bucle, un identificador o una solicitud está completamente controlada por la biblioteca y solucionarlo podría dañar las cosas rápidamente.
Dicho esto, volverse sin formato es cuestión de utilizar las funciones miembro raw
:
bucle automático = uvw::loop::get_default();auto tcp = bucle->recurso<uvw::tcp_handle>();uv_loop_t *raw = bucle->raw();uv_tcp_t *handle = tcp->raw() ;
Siga el camino crudo bajo su propio riesgo, pero no espere ningún soporte en caso de errores.
¿Está interesado en herramientas y bibliotecas adicionales basadas en uvw
? Entonces puede que le resulte útil lo siguiente:
uvw_net
: una biblioteca de red con una colección de clientes (HTTP/Modbus/SunSpec) que también incluye implementaciones de descubrimiento como dns-sd/mdns.
Siéntase libre de agregar su herramienta a la lista si lo desea.
Si desea contribuir, envíe parches como solicitudes de extracción contra la rama maestra.
Consulte la lista de contribuyentes para ver quiénes han participado hasta ahora.
Código y documentación Copyright (c) 2016-2024 Michele Caini.
Logotipo Copyright (c) 2018-2021 Richard Caseres.
Código y documentación publicados bajo la licencia MIT.
Logotipo publicado bajo CC BY-SA 4.0.
Si quieres apoyar este proyecto, puedes ofrecerme un espresso.
Si crees que no es suficiente, no dudes en ayudarme como prefieras.