From 42eeda8b8649f84b3dbcdcc77dbf5ccd58d7da5a Mon Sep 17 00:00:00 2001 From: levalup Date: Mon, 8 Jul 2024 19:41:59 +0800 Subject: [PATCH] add examples. bug fix. --- CHANGELOG.md | 84 +++++---- README.md | 3 +- examples/CMakeLists.txt | 6 + examples/pipe.cpp | 89 +++++++++ examples/tcp.cpp | 115 ++++++++++++ examples/tty_stdout.cpp | 28 +++ examples/udp.cpp | 104 +++++++++++ examples/work.cpp | 23 +++ include/uvcxx/buf.h | 20 ++- include/uvcxx/connect.h | 1 + include/uvcxx/cxx/except.h | 18 ++ include/uvcxx/cxx/sockaddr.h | 319 +++++++++++++++++++++++++++++++++ include/uvcxx/fs.h | 10 ++ include/uvcxx/pipe.h | 40 ++--- include/uvcxx/poll.h | 33 +++- include/uvcxx/process.h | 1 + include/uvcxx/stream.h | 117 ++++++------ include/uvcxx/tcp.h | 30 +++- include/uvcxx/tty.h | 24 ++- include/uvcxx/udp.h | 16 +- include/uvcxx/utils/callback.h | 30 ++-- include/uvcxx/utils/promise.h | 30 ++-- tests/CMakeLists.txt | 6 + 23 files changed, 962 insertions(+), 185 deletions(-) create mode 100644 examples/pipe.cpp create mode 100644 examples/tcp.cpp create mode 100644 examples/tty_stdout.cpp create mode 100644 examples/udp.cpp create mode 100644 examples/work.cpp create mode 100644 include/uvcxx/cxx/sockaddr.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 97967c0..bca5f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,20 +9,28 @@ ### New features - Add `callback` method on handles. - - Use `callback` method to directly obtain callback for such as `async`, `check` or `timer`. - - Use `listen_callback` method to obtain the callback for stream's `listen`. - - You can set up handling functions independently before performing any work. - - This can be very useful when operations may involve multiple starts and stops. + - Use `callback` method to directly obtain callback for such as `async`, `check` or `timer`. + - Use `listen_callback` method to obtain the callback for stream's `listen`. + - You can set up handling functions independently before performing any work. + - This can be very useful when operations may involve multiple starts and stops. - Add examples. +- `pipe`, `tcp` change state to `detached` after `connected`. + - This is an experimental feature and may be adjusted before the official release. ## Bug fix - Fix the variable usage error in the `lib_t::open(string)` method. +- Fix `pipe_connect` forgot set data field. ## Break changes - Change return value of `signal_t::start_oneshot`. - - `callback` -> `promise` + - `callback` -> `promise` +- Remove `acceptable_stream` abstract class layer. + - Practical experience proves that this abstraction layer does not significantly solve the problem of usability, + but also introduces some unnecessary complexity. +- Remove `stream_t::accept`. + - Overload the function in specific subtypes to avoid ambiguous semantics inherent in it. -------------------------------- @@ -33,19 +41,19 @@ ### New features - Add CI workflows. - - Test latest ubuntu/windows gcc, clang and cl. - - Test classic GCC 4.8.5 with libuv 1.44.2. - - Test GCC 5, 6, 7, 8, 9, 10, 11, 12, latest. - - Test GCC with -std=11, 14, 17. - - Test MacOS with Xcode(Clang). - - Test MinGW. - - Test MSVC with /std:11, 14, 17. - - Test single header. + - Test latest ubuntu/windows gcc, clang and cl. + - Test classic GCC 4.8.5 with libuv 1.44.2. + - Test GCC 5, 6, 7, 8, 9, 10, 11, 12, latest. + - Test GCC with -std=11, 14, 17. + - Test MacOS with Xcode(Clang). + - Test MinGW. + - Test MSVC with /std:11, 14, 17. + - Test single header. ### Bug fix - Fix the issue that the generated single header file may vary in different environments, - - which leads to compilation errors. + - which leads to compilation errors. -------------------------------- @@ -57,29 +65,30 @@ - Tested and passed on gcc `4.8.5` with `libuv` `v1.44.2`. - Enhanced the capabilities of `promise/callback`. - - It can properly handle movable but non-copyable objects. - - It supports passing references. + - It can properly handle movable but non-copyable objects. + - It supports passing references. - Add `merge.py` to merge the code of uvcxx into a single header file. - - You can find `uvcxx-single.h` in the release package. + - You can find `uvcxx-single.h` in the release package. ### Bug fix - Fix std::async UB with no policy in early C++ standard. - - Add `std::launch::async`. + - Add `std::launch::async`. ### Break changes -- The release of associated resources, such as `uv_fs_req_cleanup`, will be called before the `finally` of the `promise`. - - This adjustment is necessary for compilation on gcc `4.8.x`. - - Except for the deprecated usage, this modification has no impact in most scenarios. - - Bote: do not save related resources in the `then` function and use in `finally`. +- The release of associated resources, such as `uv_fs_req_cleanup`, will be called before the `finally` of + the `promise`. + - This adjustment is necessary for compilation on gcc `4.8.x`. + - Except for the deprecated usage, this modification has no impact in most scenarios. + - Bote: do not save related resources in the `then` function and use in `finally`. - Add `status` check on `handle` callback and change callback type. - - `fs_event` start return `callback`. - - `fs_poll` start return `callback`. - - `poll` start return `callback`. - - `stream` listen return `callback<>`. - - Handle status issue with `except(...)`. + - `fs_event` start return `callback`. + - `fs_poll` start return `callback`. + - `poll` start return `callback`. + - `stream` listen return `callback<>`. + - Handle status issue with `except(...)`. -------------------------------- @@ -90,12 +99,12 @@ ### New features - Cover about all APIs of `libuv`. - - Add `uv::os` wrapper for `uv_os_xxx`. - - Add most of miscellaneous utilities. + - Add `uv::os` wrapper for `uv_os_xxx`. + - Add most of miscellaneous utilities. - Downwards to C++11, while keep compatibility to C++14 and C++17. - - The minimum GCC version tested is 5.4. - - CMAKE_CXX_STANDARD in (11 14 17), have passed the tests on MSVC (19.34), GCC (11), and Clang (15). - - More compatibility tests are pending. + - The minimum GCC version tested is 5.4. + - CMAKE_CXX_STANDARD in (11 14 17), have passed the tests on MSVC (19.34), GCC (11), and Clang (15). + - More compatibility tests are pending. ### Bug fix @@ -122,7 +131,7 @@ ### Break changes -- Removed some overloaded versions of `fs::read` and `fs::write`. Simplified by using `buffer_like` type. +- Removed some overloaded versions of `fs::read` and `fs::write`. Simplified by using `buffer_like` type. -------------------------------- @@ -133,9 +142,9 @@ ### New features - Cover about `[97%]` APIs of `libuv`. - - Add `thread`, `cond`, `mutex`, `rwlock`, `sem`, `once` and `barrier`. - - Add `process`. - - Finish `fs`. + - Add `thread`, `cond`, `mutex`, `rwlock`, `sem`, `once` and `barrier`. + - Add `process`. + - Finish `fs`. - Add scripts to calculate coverage and compatibility of `libuv`. - Designed and implemented a more secure resource lifecycle management. - Verified compilation in latest ubuntu `gcc` and `clang`, windows `msvc` and `mingw`. @@ -162,7 +171,8 @@ ### New features -- The inheritance relationship of objects is basically stable, but there are still some interfaces that need to be encapsulated, and the details of encapsulation may also change. +- The inheritance relationship of objects is basically stable, but there are still some interfaces that need to be + encapsulated, and the details of encapsulation may also change. ### Bug fix diff --git a/README.md b/README.md index e046631..e1970e0 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,8 @@ The `handle` can continue to work without any external references, so that expli See [lifecycle.md](docs/lifecycle.md) for more details. -**Be careful** not to capture the handle itself by value in the lambda of the callback function, unless you are very clear about the lifecycle of the handle. +Remember, it is always a good practice to explicitly call `close` at any time, +unless you are very clear about the lifecycle of the handle. ## 2. Compatibility diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0b53dee..8cf7ba7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -71,6 +71,12 @@ if (WALL) endif () endif () +if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + foreach (target ${TARGETS}) + set_target_properties(${target} PROPERTIES OUTPUT_NAME "debug-${target}") + endforeach () +endif () + if (WERROR) if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") foreach (target ${TARGETS}) diff --git a/examples/pipe.cpp b/examples/pipe.cpp new file mode 100644 index 0000000..f8a871c --- /dev/null +++ b/examples/pipe.cpp @@ -0,0 +1,89 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include + +#include "uvcxx/pipe.h" +#include "uvcxx/buf.h" +#include "uvcxx/timer.h" +#include "uvcxx/utilities.h" + +int main() { + // setting + std::string pipe_name = "a.pipe"; + auto server_time_ms = 1000; + + (void) std::remove(pipe_name.c_str()); + + uv::loop_t server_loop; + uv::loop_t client_loop; + + // server + { + uv::pipe_t server(server_loop, false); + server.bind(pipe_name); + + server.listen(128).call([=]() mutable { + auto conn = server.accept(false); + + uv::buf_t server_buf; + conn.alloc().call([=](size_t size, uv_buf_t *buf) mutable { + server_buf.resize(size); + *buf = server_buf; + }); + conn.read_start().call([=](ssize_t nread, const uv_buf_t *buf) mutable { + std::cout << "server read: " << std::string(buf->base, nread) << std::endl; + }).except([]() { + }).except([=]() mutable { + conn.close(nullptr); + }).except([=]() mutable { + conn.close(nullptr); + }); + }).except([=](const std::exception &e) { + std::cout << "[ERROR] listen named pipe " << pipe_name << " failed. " << e.what() << std::endl; + throw uvcxx::close_handle(); + }).finally([=]() { + (void) std::remove(pipe_name.c_str()); + }); + + std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; + uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + server.close(nullptr); + throw uvcxx::close_handle(); // close timer + }); + } + + // client + for (int i = 0; i < 5; ++i) { + auto msg = uvcxx::catstr("hello~", i); + + uv::pipe_t client(client_loop, false); + client.connect(pipe_name).then([=]() mutable { + client.write(msg).then([=]() { + std::cout << "client write: " << msg << std::endl; + }).finally([=]() mutable { + client.close(nullptr); + }); + }).except([=]() mutable { + std::cout << "[ERROR] can not connect to " << pipe_name << std::endl; + client.close(nullptr); + }); + } + + auto server_run = std::async(std::launch::async, [&]() { + server_loop.run(); + std::cout << "[INFO] server exit" << std::endl; + }); + + auto client_run = std::async(std::launch::async, [&]() { + client_loop.run(); + std::cout << "[INFO] client exit" << std::endl; + }); + + server_run.wait(); + client_run.wait(); + + return 0; +} diff --git a/examples/tcp.cpp b/examples/tcp.cpp new file mode 100644 index 0000000..f82d7b7 --- /dev/null +++ b/examples/tcp.cpp @@ -0,0 +1,115 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include + +#include "uvcxx/cxx/sockaddr.h" +#include "uvcxx/tcp.h" +#include "uvcxx/buf.h" +#include "uvcxx/timer.h" +#include "uvcxx/utilities.h" + +uvcxx::any_address_t getsockname(const uv::tcp_t &tcp) { + uvcxx::any_address_t name; + auto len = name.len(); + (void) tcp.getsockname(name, &len); + return name; +} + +uvcxx::any_address_t getpeername(const uv::tcp_t &tcp) { + uvcxx::any_address_t name; + auto len = name.len(); + (void) tcp.getpeername(name, &len); + return name; +} + +int main() { + // setting + std::string localhost = "127.0.0.1"; + int port = 0; + auto server_time_ms = 1000; + + uv::loop_t server_loop; + uv::loop_t client_loop; + + // server + { + // make address support IPv4 and IPv6 + auto addr = uvcxx::make_address("0.0.0.0", port); + // if work IPv4 only, simplify code as following + // uvcxx::IPv4 addr("0.0.0.0", port); // or uvcxx::IPv4(uvcxx::address_t::ANY, port); + + uv::tcp_t server(server_loop); + server.bind(addr, 0); + + auto server_sock = getsockname(server); + port = server_sock.port(); + + std::cout << "[INFO] server start listen: " << server_sock << std::endl; + + server.listen(128).call([=]() mutable { + auto conn = server.accept(false); + + std::cout << "[INFO] received [" << getsockname(conn) << " <- " << getpeername(conn) << "]" << std::endl; + + uv::buf_t server_buf; + conn.alloc().call([=](size_t size, uv_buf_t *buf) mutable { + server_buf.resize(size); + *buf = server_buf; + }); + conn.read_start().call([=](ssize_t nread, const uv_buf_t *buf) mutable { + std::cout << "server read: " << std::string(buf->base, nread) << std::endl; + }).except([]() { + }).except([=]() mutable { + conn.close(nullptr); + }).except([=]() mutable { + conn.close(nullptr); + }); + }).except([=](const std::exception &e) { + std::cout << "[ERROR] listen " << addr << " failed. " << e.what() << std::endl; + throw uvcxx::close_handle(); + }); + + std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; + uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + server.close(nullptr); + throw uvcxx::close_handle(); // close timer + }); + } + + // client + for (int i = 0; i < 5; ++i) { + auto addr = uvcxx::make_address(localhost, port); + + auto msg = uvcxx::catstr("hello~", i); + + uv::tcp_t client(client_loop, false); + client.connect(addr).then([=]() mutable { + client.write(msg).then([=]() { + std::cout << "client write: " << msg << std::endl; + }).finally([=]() mutable { + client.close(nullptr); + }); + }).except([=]() mutable { + std::cout << "[ERROR] can not connect to " << addr << std::endl; + client.close(nullptr); + }); + } + + auto server_run = std::async(std::launch::async, [&]() { + server_loop.run(); + std::cout << "[INFO] server exit" << std::endl; + }); + + auto client_run = std::async(std::launch::async, [&]() { + client_loop.run(); + std::cout << "[INFO] client exit" << std::endl; + }); + + server_run.wait(); + client_run.wait(); + + return 0; +} diff --git a/examples/tty_stdout.cpp b/examples/tty_stdout.cpp new file mode 100644 index 0000000..8b7e4f0 --- /dev/null +++ b/examples/tty_stdout.cpp @@ -0,0 +1,28 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include "uvcxx/tty.h" +#include "uvcxx/utilities.h" +#include "uvcxx/cxx/to_string.h" + +int main() { + if (uv::guess_handle(1) != UV_TTY) { + std::cout << "fd = 1 is " << uvcxx::to_string(uv::guess_handle(1)) << std::endl; + return -1; + } + + uv::loop_t loop; + + uv::tty_t tty(loop, 1); + tty.set_mode(UV_TTY_MODE_NORMAL); + + tty.try_write("\033[31m" + "hello~" + "\033[0m"); + tty.close(nullptr); + + uv::tty::reset_mode(); + return loop.run(); +} diff --git a/examples/udp.cpp b/examples/udp.cpp new file mode 100644 index 0000000..4a2c108 --- /dev/null +++ b/examples/udp.cpp @@ -0,0 +1,104 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include + +#include "uvcxx/cxx/sockaddr.h" +#include "uvcxx/cxx/to_string.h" +#include "uvcxx/udp.h" +#include "uvcxx/buf.h" +#include "uvcxx/timer.h" +#include "uvcxx/utilities.h" + +uvcxx::any_address_t getsockname(const uv::udp_t &tcp) { + uvcxx::any_address_t name; + auto len = name.len(); + (void) tcp.getsockname(name, &len); + return name; +} + +uvcxx::any_address_t getpeername(const uv::udp_t &tcp) { + uvcxx::any_address_t name; + auto len = name.len(); + (void) tcp.getpeername(name, &len); + return name; +} + +int main() { + // setting + std::string localhost = "127.0.0.1"; + int port = 0; + auto server_time_ms = 1000; + + uv::loop_t server_loop; + uv::loop_t client_loop; + + // server + { + // make address support IPv4 and IPv6 + auto addr = uvcxx::make_address("0.0.0.0", port); + // if work IPv4 only, simplify code as following + // uvcxx::IPv4 addr("0.0.0.0", port); // or uvcxx::IPv4(uvcxx::address_t::ANY, port); + + uv::udp_t server(server_loop); + server.bind(addr, 0); + + auto server_sock = getsockname(server); + port = server_sock.port(); + + std::cout << "[INFO] server start listen: " << server_sock << std::endl; + + uv::buf_t server_buf; + server.alloc().call([=](size_t size, uv_buf_t *buf) mutable { + server_buf.resize(size); + *buf = server_buf; + }); + server.recv_start().call( + [=](ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, uv_udp_flags flags) mutable { + // std::cout << "[INFO] received [" << server_sock << " <- " << uvcxx::any_address_t(addr) << "]" << std::endl; + std::cout << "server read: " << std::string(buf->base, nread) << " with " + << uvcxx::to_string(uv_udp_flags(flags)) << std::endl; + }).except([=](const std::exception &e) { + std::cout << "[ERROR] recv " << addr << " failed. " << e.what() << std::endl; + throw uvcxx::close_handle(); + }); + + std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; + uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + server.close(nullptr); + throw uvcxx::close_handle(); // close timer + }); + } + + // client + for (int i = 0; i < 5; ++i) { + auto addr = uvcxx::make_address(localhost, port); + + auto msg = uvcxx::catstr("hello~", i); + + uv::udp_t client(client_loop, false); + client.connect(addr); + client.send(msg, nullptr).then([=]() { + std::cout << "client write: " << msg << std::endl; + }).finally([=]() mutable { + client.close(nullptr); + }); + } + + auto server_run = std::async(std::launch::async, [&]() { + server_loop.run(); + std::cout << "[INFO] server exit" << std::endl; + }); + + auto client_run = std::async(std::launch::async, [&]() { + client_loop.run(); + std::cout << "[INFO] client exit" << std::endl; + }); + + server_run.wait(); + client_run.wait(); + + return 0; +} diff --git a/examples/work.cpp b/examples/work.cpp new file mode 100644 index 0000000..006d677 --- /dev/null +++ b/examples/work.cpp @@ -0,0 +1,23 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include "uvcxx/os.h" +#include "uvcxx/work.h" +#include "uvcxx/utilities.h" + +int main() { + uv::os::setenv("UV_THREADPOOL_SIZE", "8"); + + uv::loop_t loop; + + for (int i = 0; i < 10; ++i) { + uv::queue_work(loop).then([i]() { + uv::sleep(rand() % 10); + std::cout << uvcxx::catstr("work-", i) << std::endl; + }); + } + + return loop.run(); +} diff --git a/include/uvcxx/buf.h b/include/uvcxx/buf.h index 9ed1bea..c8f9b4e 100644 --- a/include/uvcxx/buf.h +++ b/include/uvcxx/buf.h @@ -85,6 +85,15 @@ namespace uv { }; } + template::value, int>::type = 0> + inline uv_buf_t buf_init(const void *base, I len) { + // cover uv_buf_init + uv_buf_t buf{}; + buf.base = (decltype(buf.base)) (base); + buf.len = (decltype(buf.len)) (len); + return buf; + } + /** * This type is a reference type, and all copied objects use the same buffer, * which is not thread-safe. @@ -141,16 +150,9 @@ namespace uv { void memset(int v) { std::memset(data(), v, size()); } - }; - template::value, int>::type = 0> - inline uv_buf_t buf_init(const void *base, I len) { - // cover uv_buf_init - uv_buf_t buf{}; - buf.base = (decltype(buf.base)) (base); - buf.len = (decltype(buf.len)) (len); - return buf; - } + operator uv_buf_t() const { return buf_init(data(), size()); } + }; } namespace uvcxx { diff --git a/include/uvcxx/connect.h b/include/uvcxx/connect.h index b3b96c0..cde9200 100644 --- a/include/uvcxx/connect.h +++ b/include/uvcxx/connect.h @@ -64,6 +64,7 @@ namespace uv { uv_pipe_connect(req, handle, name, connect_t::callback_t::raw_callback); delete_data.release(); + ((uv_connect_t *) req)->data = data; return data->promise.promise(); } diff --git a/include/uvcxx/cxx/except.h b/include/uvcxx/cxx/except.h index 8979ee5..76085fb 100644 --- a/include/uvcxx/cxx/except.h +++ b/include/uvcxx/cxx/except.h @@ -82,6 +82,24 @@ namespace uvcxx { private: int m_errcode = 0; }; + +#define uvcxx_define_errcode(name, code) \ + class name : public ::uvcxx::errcode { \ + public: \ + using self = name; \ + using supper = errcode; \ + name() : supper(code) {} \ + } + + uvcxx_define_errcode(E_EOF, UV_EOF); + + uvcxx_define_errcode(E_EAGAIN, UV_EAGAIN); + + uvcxx_define_errcode(E_EADDRINUSE, UV_EADDRINUSE); + + uvcxx_define_errcode(E_EBADF, UV_EBADF); + + uvcxx_define_errcode(E_ENOTSOCK, UV_ENOTSOCK); } #if defined(UVCXX_NO_EXCEPTION) diff --git a/include/uvcxx/cxx/sockaddr.h b/include/uvcxx/cxx/sockaddr.h new file mode 100644 index 0000000..101af5c --- /dev/null +++ b/include/uvcxx/cxx/sockaddr.h @@ -0,0 +1,319 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#ifndef LIBUVCXX_CXX_SOCKADDR_H +#define LIBUVCXX_CXX_SOCKADDR_H + +#include +#include +#include + +#include + +#include "./string.h" +#include "./wrapper.h" +#include "../utils/pencil_box.h" +#include "../utils/standard.h" + +/** + * This header file exposes two macros, `AF_INET` and `AF_INET6`, + * which are related to platform implementation. Generally, + * these two macros will be available after including `uv.h`. + */ + +namespace uvcxx { + enum class Family : int { + IPv4 = AF_INET, + IPv6 = AF_INET6, + }; + + class address_t { + public: + enum { + ANY, + }; + + UVCXX_NODISCARD + Family family() const { + return Family(addr()->sa_family); + } + + UVCXX_NODISCARD + virtual sockaddr *addr() const = 0; + + UVCXX_NODISCARD + virtual int len() const = 0; + + UVCXX_NODISCARD + virtual std::string ip() const = 0; + + UVCXX_NODISCARD + virtual int port() const = 0; + + UVCXX_NODISCARD + virtual std::string str() const = 0; + + operator sockaddr *() const { return this->addr(); } + }; + + inline std::ostream &operator<<(std::ostream &out, const address_t &addr) { + return out << addr.str(); + } + + /** + * Almost all platforms that support sockets provide an implementation of `htons`, + * which is just used here to handle possible boundary cases. + */ + inline uint16_t builtin_htons(int p) { + union { + uint8_t a[2]; + uint16_t b = 0x1234; + } test; + if (test.a[0] == 0x12) { + return p; + } else { + return (uint16_t)((((uint16_t)(p) & 0xff00U) >> 8) | + (((uint16_t)(p) & 0x00ffU) << 8)); + } + } + + inline uint16_t builtin_ntohs(int p) { + return builtin_htons(p); + } + +#pragma push_macro("htons") +#pragma push_macro("ntohs") + +#ifndef htons +#define htons builtin_htons +#endif +#ifndef ntohs +#define ntohs builtin_htons +#endif + + class any_address_t : public address_t { + public: + any_address_t() { + std::memset(&m_storage, 0, sizeof(m_storage)); + m_size = sizeof(m_storage); + } + + any_address_t(const sockaddr *addr, int len) { + std::memcpy(&m_storage, addr, len); + m_size = len; + } + + explicit any_address_t(const sockaddr *addr) { + std::memcpy(&m_storage, addr, addr->sa_len); + m_size = addr->sa_len; + } + + UVCXX_NODISCARD + sockaddr *addr() const final { + return (sockaddr *) (&m_storage); + } + + UVCXX_NODISCARD + int len() const final { + return m_size; + } + + UVCXX_NODISCARD + std::string ip() const final { + if (m_storage.ss_family == AF_INET) { // ipv4 + char dst[UV_IF_NAMESIZE] = {0}; + (void) uv_ip4_name((sockaddr_in *) (&m_storage), dst, sizeof(dst)); + return dst; + } else if (m_storage.ss_family == AF_INET6) { // ipv6 + char dst[UV_IF_NAMESIZE] = {0}; + (void) uv_ip6_name((sockaddr_in6 *) (&m_storage), dst, sizeof(dst)); + return dst; + } else { + return ""; + } + } + + UVCXX_NODISCARD + int port() const final { + if (m_storage.ss_family == AF_INET) { // ipv4 + auto ns_port = ((sockaddr_in *) (&m_storage))->sin_port; + return ntohs(ns_port); + } else if (m_storage.ss_family == AF_INET6) { // ipv6 + auto ns_port = ((sockaddr_in6 *) (&m_storage))->sin6_port; + return ntohs(ns_port); + } else { + return 0; + } + } + + UVCXX_NODISCARD + std::string str() const final { + if (m_storage.ss_family == AF_INET) { // ipv4 + char ip[UV_IF_NAMESIZE] = {0}; + (void) uv_ip4_name((sockaddr_in *) (&m_storage), ip, sizeof(ip)); + auto ns_port = ((sockaddr_in *) (&m_storage))->sin_port; + auto h_port = ntohs(ns_port); + return catstr(ip, ":", h_port); + } else if (m_storage.ss_family == AF_INET6) { // ipv6 + char ip[UV_IF_NAMESIZE] = {0}; + (void) uv_ip6_name((sockaddr_in6 *) (&m_storage), ip, sizeof(ip)); + auto ns_port = ((sockaddr_in6 *) (&m_storage))->sin6_port; + auto h_port = ntohs(ns_port); + return catstr("[", ip, "]:", h_port); + } else { + return ""; + } + } + + explicit operator sockaddr_in *() const { return (sockaddr_in *) (&m_storage); } + + explicit operator sockaddr_in6 *() const { return (sockaddr_in6 *) (&m_storage); } + + sockaddr *raddr() { return (sockaddr *) &m_storage; } + + int &rlen() { return m_size; } + + private: + sockaddr_storage m_storage{}; + int m_size = 0; + + static_assert(sizeof(sockaddr_storage) >= sizeof(sockaddr_in)); + static_assert(sizeof(sockaddr_storage) >= sizeof(sockaddr_in6)); + }; + + class IPv4 : public address_t { + public: + IPv4(string ip, int port) { + UVCXX_APPLY_STRICT(uv_ip4_addr(ip, port, &m_addr), "failed to set address ", ip, " ", port); + } + + IPv4(decltype(address_t::ANY), int port) { + UVCXX_APPLY_STRICT(uv_ip4_addr("0.0.0.0", port, &m_addr), "failed to set address 0.0.0.0 ", port); + } + + UVCXX_NODISCARD + sockaddr *addr() const final { + return (struct sockaddr *) (&m_addr); + } + + UVCXX_NODISCARD + int len() const final { + return int(sizeof(m_addr)); + } + + UVCXX_NODISCARD + std::string ip() const final { + char dst[UV_IF_NAMESIZE] = {0}; + (void) uv_ip4_name(*this, dst, sizeof(dst)); + return dst; + } + + UVCXX_NODISCARD + int port() const final { + auto ns_port = m_addr.sin_port; + return ntohs(ns_port); + } + + UVCXX_NODISCARD + std::string str() const final { + char ip[UV_IF_NAMESIZE] = {0}; + (void) uv_ip4_name(&m_addr, ip, sizeof(ip)); + auto ns_port = m_addr.sin_port; + auto h_port = ntohs(ns_port); + return catstr(ip, ":", h_port); + } + + operator sockaddr_in *() const { return (sockaddr_in *) &m_addr; } + + operator any_address_t() const { + return {addr(), len()}; + } + + private: + sockaddr_in m_addr{}; + }; + + class IPv6 : public address_t { + public: + IPv6(string ip, int port) { + UVCXX_APPLY_STRICT(uv_ip6_addr(ip, port, &m_addr), "failed to set address ", ip, " ", port); + } + + IPv6(decltype(address_t::ANY), int port) { + UVCXX_APPLY_STRICT(uv_ip6_addr("::", port, &m_addr), "failed to set address :: ", port); + } + + UVCXX_NODISCARD + sockaddr *addr() const final { + return (struct sockaddr *) (&m_addr); + } + + UVCXX_NODISCARD + int len() const final { + return int(sizeof(m_addr)); + } + + UVCXX_NODISCARD + std::string ip() const final { + char dst[UV_IF_NAMESIZE] = {0}; + (void) uv_ip6_name(*this, dst, sizeof(dst)); + return dst; + } + + UVCXX_NODISCARD + int port() const final { + auto ns_port = m_addr.sin6_port; + return ntohs(ns_port); + } + + UVCXX_NODISCARD + std::string str() const final { + char ip[UV_IF_NAMESIZE] = {0}; + (void) uv_ip6_name(&m_addr, ip, sizeof(ip)); + auto ns_port = m_addr.sin6_port; + auto h_port = ntohs(ns_port); + return catstr("[", ip, "]:", h_port); + } + + operator sockaddr_in6 *() const { return (sockaddr_in6 *) &m_addr; } + + operator any_address_t() const { + return {addr(), len()}; + } + + private: + sockaddr_in6 m_addr{}; + }; + +#pragma pop_macro("htons") +#pragma pop_macro("ntohs") + + inline any_address_t make_address(string ip, int port) { + static const std::regex ipv6( + R"(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|)" + R"(((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f)" + R"(]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4])" + R"(\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f)" + R"(]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(()" + R"([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\)" + R"(d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[)" + R"(0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-)" + R"(5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:)" + R"([0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" + R"()){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d)" + R"(|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$)"); + static const std::regex ipv4( + R"(^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)"); + if (std::regex_match(ip.c_str, ipv4)) { + return IPv4(ip, port); + } + if (std::regex_match(ip.c_str, ipv6)) { + return IPv6(ip, port); + } + throw uvcxx::errcode(UV_EPERM, "can not tell it's IPv4 or IPv6 address: \"", ip, "\""); + } +} + +#endif //LIBUVCXX_CXX_SOCKADDR_H diff --git a/include/uvcxx/fs.h b/include/uvcxx/fs.h index f199539..08c78e8 100644 --- a/include/uvcxx/fs.h +++ b/include/uvcxx/fs.h @@ -286,6 +286,7 @@ namespace uv { uv_file file, std::initializer_list bufs, int64_t offset, std::nullptr_t) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return read(nullptr, req, file, buffers.data(), (unsigned int) buffers.size(), offset, nullptr); } @@ -302,6 +303,7 @@ namespace uv { const loop_t &loop, const fs_t &req, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return read(loop, req, file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -316,6 +318,7 @@ namespace uv { inline uvcxx::promise read( uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return read(file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -332,6 +335,7 @@ namespace uv { const loop_t &loop, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return read(loop, file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -348,6 +352,7 @@ namespace uv { const fs_t &req, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return read(req, file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -397,6 +402,7 @@ namespace uv { uv_file file, std::initializer_list bufs, int64_t offset, std::nullptr_t) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return write(nullptr, req, file, buffers.data(), (unsigned int) buffers.size(), offset, nullptr); } @@ -413,6 +419,7 @@ namespace uv { const loop_t &loop, const fs_t &req, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return write(loop, req, file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -427,6 +434,7 @@ namespace uv { inline uvcxx::promise write( uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return write(file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -443,6 +451,7 @@ namespace uv { const loop_t &loop, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return write(loop, file, buffers.data(), (unsigned int) buffers.size(), offset); } @@ -459,6 +468,7 @@ namespace uv { const fs_t &req, uv_file file, std::initializer_list bufs, int64_t offset) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return write(req, file, buffers.data(), (unsigned int) buffers.size(), offset); } diff --git a/include/uvcxx/pipe.h b/include/uvcxx/pipe.h index a74f997..2b07a8f 100644 --- a/include/uvcxx/pipe.h +++ b/include/uvcxx/pipe.h @@ -12,10 +12,10 @@ #include "stream.h" namespace uv { - class pipe_t : public inherit_handle_t { + class pipe_t : public inherit_handle_t { public: using self = pipe_t; - using supper = inherit_handle_t; + using supper = inherit_handle_t; using raw_t = uv_pipe_t; @@ -44,8 +44,8 @@ namespace uv { UVCXX_PROXY(uv_fileno(*this, fd)); } - stream_t accept() override { - self client(this->loop()); + self accept(bool ipc) { + self client(this->loop(), ipc); UVCXX_APPLY(uv_accept(*this, client), nullptr); @@ -70,52 +70,44 @@ namespace uv { return bind2(name.data, name.size, flags); } - int bind(uvcxx::string_view name, unsigned int flags) { - return bind2(name.data, name.size, flags); - } - #endif UVCXX_NODISCARD uvcxx::promise<> connect(const connect_t &req, uvcxx::string name) { - return pipe_connect(req, *this, name); + auto p = pipe_connect(req, *this, name); + return p ? (_detach_(), p) : nullptr; } UVCXX_NODISCARD uvcxx::promise<> connect(uvcxx::string name) { - return pipe_connect(*this, name); + auto p = pipe_connect(*this, name); + return p ? (_detach_(), p) : nullptr; } #if UVCXX_SATISFY_VERSION(1, 46, 0) UVCXX_NODISCARD uvcxx::promise<> connect2(const connect_t &req, const char *name, size_t namelen, unsigned int flags) { - return pipe_connect2(req, *this, name, namelen, flags); + auto p = pipe_connect2(req, *this, name, namelen, flags); + return p ? (_detach_(), p) : nullptr; } UVCXX_NODISCARD uvcxx::promise<> connect2(const char *name, size_t namelen, unsigned int flags) { - return pipe_connect2(*this, name, namelen, flags); + auto p = pipe_connect2(*this, name, namelen, flags); + return p ? (_detach_(), p) : nullptr; } UVCXX_NODISCARD uvcxx::promise<> connect2(const connect_t &req, uvcxx::string_view name, unsigned int flags) { - return pipe_connect2(req, *this, name.data, name.size, flags); + auto p = pipe_connect2(req, *this, name.data, name.size, flags); + return p ? (_detach_(), p) : nullptr; } UVCXX_NODISCARD uvcxx::promise<> connect2(uvcxx::string_view name, unsigned int flags) { - return pipe_connect2(*this, name.data, name.size, flags); - } - - UVCXX_NODISCARD - uvcxx::promise<> connect(const connect_t &req, uvcxx::string_view name, unsigned int flags) { - return pipe_connect2(req, *this, name.data, name.size, flags); - } - - UVCXX_NODISCARD - uvcxx::promise<> connect(uvcxx::string_view name, unsigned int flags) { - return pipe_connect2(*this, name.data, name.size, flags); + auto p = pipe_connect2(*this, name.data, name.size, flags); + return p ? (_detach_(), p) : nullptr; } #endif diff --git a/include/uvcxx/poll.h b/include/uvcxx/poll.h index 3da3264..702157c 100644 --- a/include/uvcxx/poll.h +++ b/include/uvcxx/poll.h @@ -20,20 +20,28 @@ namespace uv { } self &init(const loop_t &loop, int fd) { + auto data = get_data(); + if (data->initialized) { + UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "duplicated poll_t initialization"); + } // To directly start after init, there is no path to return the error code instead. // So an exception is directly thrown. This feature may be modified in the future. UVCXX_APPLY_STRICT(uv_poll_init(loop, *this, fd)); _attach_close_(); - get_data()->initialized = true; + data->initialized = true; return *this; } self &init_socket(const loop_t &loop, uv_os_sock_t socket) { + auto data = get_data(); + if (data->initialized) { + UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "duplicated poll_t initialization"); + } // To directly start after init, there is no path to return the error code instead. // So an exception is directly thrown. This feature may be modified in the future. UVCXX_APPLY_STRICT(uv_poll_init_socket(loop, *this, socket)); _attach_close_(); - get_data()->initialized = true; + data->initialized = true; return *this; } @@ -65,17 +73,30 @@ namespace uv { } void stop() { + auto data = get_data(); + + if (!data->initialized) { + UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "should call `init` or `init_socket` first"); + } + (void) uv_poll_stop(*this); _attach_close_(); } private: static void raw_callback(raw_t *handle, int status, int events) { - auto data = (data_t *) (handle->data); - if (status < 0) { - (void) data->start_cb.raise(status); - } else { + auto data = (data_t * )(handle->data); + if (status >= 0) { data->start_cb.emit(events); + return; + } + switch (status) { + case UV_EAGAIN: + data->start_cb.raise(); + break; + default: + data->start_cb.raise(status); + break; } } diff --git a/include/uvcxx/process.h b/include/uvcxx/process.h index 86b1d31..5ebcc1b 100644 --- a/include/uvcxx/process.h +++ b/include/uvcxx/process.h @@ -145,6 +145,7 @@ namespace uv { if (size > m_cxx.stdio.size()) { uv_stdio_container_t ignore{}; ignore.flags = UV_IGNORE; + ignore.data.fd = -1; m_cxx.stdio.resize(size, ignore); } m_cxx.stdio[i] = *io; diff --git a/include/uvcxx/stream.h b/include/uvcxx/stream.h index 54f92ef..d982563 100644 --- a/include/uvcxx/stream.h +++ b/include/uvcxx/stream.h @@ -6,6 +6,8 @@ #ifndef LIBUVCXX_STREAM_H #define LIBUVCXX_STREAM_H +#include "cxx/buffer.h" +#include "connect.h" #include "handle.h" #include "shutdown.h" #include "write.h" @@ -34,11 +36,21 @@ namespace uv { return ::uv::shutdown(*this); } + /** + * @return uvcxx::callback<> + * @throws E_EADDRINUSE, E_EBADF, E_ENOTSOCK + */ UVCXX_NODISCARD uvcxx::callback<> listen_callback() { return get_data()->listen_cb.callback(); } + /** + * Start listening for incoming connections. backlog indicates the number of connections the kernel might queue, same as listen(2). + * @param backlog the number of connections the kernel might queue + * @return uvcxx::callback<> + * @throws E_EADDRINUSE, E_EBADF, E_ENOTSOCK + */ UVCXX_NODISCARD uvcxx::callback<> listen(int backlog) { auto data = get_data(); @@ -53,10 +65,6 @@ namespace uv { return data->listen_cb.callback(); } - int accept(stream_t &client) { - UVCXX_PROXY(uv_accept(*this, client)); - } - UVCXX_NODISCARD uvcxx::callback alloc_callback() { return get_data()->alloc_cb.callback(); @@ -75,11 +83,20 @@ namespace uv { return get_data()->alloc_cb.callback().call(nullptr); } + /** + * @return uvcxx::callback + * @throws E_EOF, E_EAGAIN + */ UVCXX_NODISCARD uvcxx::callback read_callback() { return get_data()->read_cb.callback(); } + /** + * Read data from an incoming stream. + * @return uvcxx::callback + * @throws E_EOF, E_EAGAIN + */ UVCXX_NODISCARD uvcxx::callback read_start() { auto data = get_data(); @@ -122,6 +139,7 @@ namespace uv { UVCXX_NODISCARD uvcxx::promise<> write(const write_t &req, std::initializer_list bufs) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return this->write(req, buffers.data(), (unsigned int) buffers.size()); } @@ -139,6 +157,7 @@ namespace uv { UVCXX_NODISCARD uvcxx::promise<> write(std::initializer_list bufs) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return this->write(buffers.data(), (unsigned int) buffers.size()); } @@ -159,6 +178,7 @@ namespace uv { uvcxx::promise<> write2(const write_t &req, std::initializer_list bufs, const stream_t &send_handle) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return this->write2(req, buffers.data(), (unsigned int) buffers.size(), send_handle); } @@ -176,6 +196,7 @@ namespace uv { UVCXX_NODISCARD uvcxx::promise<> write2(std::initializer_list bufs, const stream_t &send_handle) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return this->write2(buffers.data(), (unsigned int) buffers.size(), send_handle); } @@ -184,12 +205,13 @@ namespace uv { return uv_try_write(*this, bufs, nbufs); } - int try_write(uvcxx::mutable_buffer buf) { + int try_write(uvcxx::buffer buf) { return try_write(&buf.buf, 1); } - int try_write(std::initializer_list bufs) { + int try_write(std::initializer_list bufs) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return try_write(buffers.data(), (unsigned int) buffers.size()); } @@ -206,6 +228,7 @@ namespace uv { int try_write2(std::initializer_list bufs, const stream_t &send_handle) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return try_write2(buffers.data(), (unsigned int) buffers.size(), send_handle); } @@ -252,15 +275,44 @@ namespace uv { static void raw_read_callback(raw_t *handle, ssize_t nread, const uv_buf_t *buf) { auto data = (data_t * )(handle->data); - data->read_cb.emit(nread, buf); + if (nread > 0) { + data->read_cb.emit(nread, buf); + return; + } + switch (nread) { + case 0: + break; + case UV_EAGAIN: + data->read_cb.raise(); + break; + case UV_EOF: + data->read_cb.raise(); + break; + default: + data->read_cb.raise(nread); + break; + } } static void raw_listen_callback(raw_t *handle, int status) { auto data = (data_t * )(handle->data); - if (status < 0) { - (void) data->listen_cb.raise(status); - } else { + if (status >= 0) { data->listen_cb.emit(); + return; + } + switch (status) { + case UV_EADDRINUSE: + data->listen_cb.raise(); + break; + case UV_EBADF: + data->listen_cb.raise(); + break; + case UV_ENOTSOCK: + data->listen_cb.raise(); + break; + default: + data->listen_cb.raise(status); + break; } } @@ -294,51 +346,6 @@ namespace uv { }; }; - class acceptable_stream_t : public stream_t { - public: - using self = stream_t; - using supper = stream_t; - - using supper::supper; - - virtual stream_t accept() = 0; - - UVCXX_NODISCARD - uvcxx::callback accept_callback() { - return data()->accept_cb.callback(); - } - - UVCXX_NODISCARD - uvcxx::callback listen_accept(int backlog) { - auto data = this->data(); - - this->listen(backlog).call(nullptr).call([data, this]() { - data->accept_cb.emit(this->accept()); - }).except(nullptr).except([data](const std::exception_ptr &p) -> bool { - return data->accept_cb.raise(p); - }).finally(nullptr).finally([data](){ - data->accept_cb.finalize(); - }); - - return data->accept_cb.callback(); - } - - protected: - class data_t : supper::data_t { - public: - uvcxx::callback_emitter accept_cb; - - explicit data_t(acceptable_stream_t &handle) - : supper::data_t(handle) { - handle.watch(accept_cb); - } - - ~data_t() override { - accept_cb.finalize(); - } - }; - }; - UVCXX_NODISCARD inline stream_t connect_t::handle() const { return stream_t::borrow(raw()->handle); diff --git a/include/uvcxx/tcp.h b/include/uvcxx/tcp.h index f34bbeb..3cb5145 100644 --- a/include/uvcxx/tcp.h +++ b/include/uvcxx/tcp.h @@ -10,10 +10,10 @@ #include "stream.h" namespace uv { - class tcp_t : public inherit_handle_t { + class tcp_t : public inherit_handle_t { public: using self = tcp_t; - using supper = inherit_handle_t; + using supper = inherit_handle_t; tcp_t() : self(default_loop()) {} @@ -47,7 +47,7 @@ namespace uv { UVCXX_PROXY(uv_fileno(*this, fd)); } - stream_t accept() override { + self accept() { self client(this->loop()); UVCXX_APPLY(uv_accept(*this, client), nullptr); @@ -55,6 +55,18 @@ namespace uv { return client; } +#if UVCXX_SATISFY_VERSION(1, 7, 0) + + self accept(int flags) { + self client(this->loop(), flags); + + UVCXX_APPLY(uv_accept(*this, client), nullptr); + + return client; + } + +#endif + int open(uv_os_sock_t sock) { UVCXX_PROXY(uv_tcp_open(*this, sock)); } @@ -71,26 +83,28 @@ namespace uv { UVCXX_PROXY(uv_tcp_simultaneous_accepts(*this, int(enable))); } - int bind(const struct sockaddr *addr, unsigned int flags) { + int bind(const sockaddr *addr, unsigned int flags) { UVCXX_PROXY(uv_tcp_bind(*this, addr, flags)); } - int getsockname(struct sockaddr *name, int *namelen) const { + int getsockname(sockaddr *name, int *namelen) const { UVCXX_PROXY(uv_tcp_getsockname(*this, name, namelen)); } - int getpeername(struct sockaddr *name, int *namelen) const { + int getpeername(sockaddr *name, int *namelen) const { UVCXX_PROXY(uv_tcp_getpeername(*this, name, namelen)); } UVCXX_NODISCARD uvcxx::promise<> connect(const connect_t &req, const sockaddr *addr) { - return tcp_connect(req, *this, addr); + auto p = tcp_connect(req, *this, addr); + return p ? (_detach_(), p) : nullptr; } UVCXX_NODISCARD uvcxx::promise<> connect(const sockaddr *addr) { - return tcp_connect(*this, addr); + auto p = tcp_connect(*this, addr); + return p ? (_detach_(), p) : nullptr; } #if UVCXX_SATISFY_VERSION(1, 32, 0) diff --git a/include/uvcxx/tty.h b/include/uvcxx/tty.h index 5250f88..5fc690e 100644 --- a/include/uvcxx/tty.h +++ b/include/uvcxx/tty.h @@ -14,24 +14,30 @@ namespace uv { using self = tty_t; using supper = inherit_handle_t; - tty_t(uv_file fd, int unused) : self(default_loop(), fd, unused) {} +#if UVCXX_SATISFY_VERSION(1, 23, 1) - explicit tty_t(const loop_t &loop, uv_file fd, int unused) { + explicit tty_t(uv_file fd, int unused = 0) : self(default_loop(), fd, unused) {} + + explicit tty_t(const loop_t &loop, uv_file fd, int unused = 0) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_tty_init(loop, *this, fd, unused); _attach_close_(); } - int fileno(uv_os_fd_t *fd) const { - UVCXX_PROXY(uv_fileno(*this, fd)); - } +#else - stream_t accept(uv_file fd, int unused) { - self client(this->loop(), fd, unused); + explicit tty_t(uv_file fd, int readable) : self(default_loop(), fd, readable) {} - UVCXX_APPLY(uv_accept(*this, client), nullptr); + explicit tty_t(const loop_t &loop, uv_file fd, int readable) { + set_data(new data_t(*this)); //< data will be deleted in close action + (void) uv_tty_init(loop, *this, fd, readable); + _attach_close_(); + } + +#endif - return client; + int fileno(uv_os_fd_t *fd) const { + UVCXX_PROXY(uv_fileno(*this, fd)); } #if !UVCXX_SATISFY_VERSION(1, 2, 0) diff --git a/include/uvcxx/udp.h b/include/uvcxx/udp.h index 9ffea70..bf1f1d9 100644 --- a/include/uvcxx/udp.h +++ b/include/uvcxx/udp.h @@ -6,6 +6,7 @@ #ifndef LIBUVCXX_UDP_H #define LIBUVCXX_UDP_H +#include "cxx/buffer.h" #include "cxx/string.h" #include "handle.h" @@ -75,13 +76,13 @@ namespace uv { UVCXX_PROXY(uv_udp_connect(*this, addr)); } - int getpeername(struct sockaddr *name, int *namelen) const { + int getpeername(sockaddr *name, int *namelen) const { UVCXX_PROXY(uv_udp_getpeername(*this, name, namelen)); } #endif - int getsockname(struct sockaddr *name, int *namelen) const { + int getsockname(sockaddr *name, int *namelen) const { UVCXX_PROXY(uv_udp_getsockname(*this, name, namelen)); } @@ -136,6 +137,7 @@ namespace uv { UVCXX_NODISCARD uvcxx::promise<> send(const udp_send_t &req, std::initializer_list bufs, const sockaddr *addr) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return send(req, buffers.data(), (unsigned int) buffers.size(), addr); } @@ -153,6 +155,7 @@ namespace uv { UVCXX_NODISCARD uvcxx::promise<> send(std::initializer_list bufs, const sockaddr *addr) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return send(buffers.data(), (unsigned int) buffers.size(), addr); } @@ -167,6 +170,7 @@ namespace uv { int try_send(std::initializer_list bufs, const sockaddr *addr) { std::vector buffers; + buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } return try_send(buffers.data(), (unsigned int) buffers.size(), addr); } @@ -190,12 +194,12 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback recv_callback() { + uvcxx::callback recv_callback() { return get_data()->recv_cb.callback(); } UVCXX_NODISCARD - uvcxx::callback + uvcxx::callback recv_start() { UVCXX_APPLY(uv_udp_recv_start(*this, raw_alloc_callback, raw_recv_callback), nullptr); _detach_(); @@ -245,12 +249,12 @@ namespace uv { static void raw_recv_callback( raw_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) { auto data = (data_t * )(handle->data); - data->recv_cb.emit(nread, buf, addr, flags); + data->recv_cb.emit(nread, buf, addr, uv_udp_flags(flags)); } class data_t : supper::data_t { public: - uvcxx::callback_emitter recv_cb; + uvcxx::callback_emitter recv_cb; uvcxx::callback_emitter alloc_cb; explicit data_t(udp_t &handle) diff --git a/include/uvcxx/utils/callback.h b/include/uvcxx/utils/callback.h index 2925d64..81e172c 100644 --- a/include/uvcxx/utils/callback.h +++ b/include/uvcxx/utils/callback.h @@ -115,12 +115,12 @@ namespace uvcxx { if (m_on_watch) { #if UVCXX_STD_INIT_CAPTURES m_on_watch = [f = std::move(f), pre = std::move(m_on_watch)](const std::exception_ptr &p) { - return pre(p) ? true : f(p); + return pre(p) || f(p); }; #else auto pre = std::move(m_on_watch); m_on_watch = [f, pre](const std::exception_ptr &p) { - return pre(p) ? true : f(p); + return pre(p) || f(p); }; #endif } else { @@ -139,12 +139,12 @@ namespace uvcxx { if (m_on_except) { #if UVCXX_STD_INIT_CAPTURES m_on_except = [f = std::move(f), pre = std::move(m_on_except)](const std::exception_ptr &p) { - return pre(p) ? true : f(p); + return pre(p) || f(p); }; #else auto pre = std::move(m_on_except); m_on_except = [f, pre](const std::exception_ptr &p) { - return pre(p) ? true : f(p); + return pre(p) || f(p); }; #endif } else { @@ -235,7 +235,7 @@ namespace uvcxx { } self &call(std::function f) { - m_core->call([UVCXX_CAPTURE_MOVE(f)](void *p) { + m_core->call([UVCXX_CAPTURE_MOVE(f)](void *p) mutable { auto &pack = *(type *) (p); proxy_apply(f, std::move(pack)); }); @@ -266,7 +266,7 @@ namespace uvcxx { std::is_base_of::value, int>::type = 0> self &except(std::function f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) mutable -> bool { try { std::rethrow_exception(p); } catch (const E &e) { @@ -282,7 +282,7 @@ namespace uvcxx { std::is_base_of::value, int>::type = 0> self &except(std::function f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) -> void { f(); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) mutable -> void { f(); }); } self &except(std::function f) { @@ -290,7 +290,7 @@ namespace uvcxx { } self &except(std::function f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) mutable -> bool { return f(); })); } @@ -316,7 +316,7 @@ namespace uvcxx { decltype(std::declval()(std::declval()...)), void>::value, int>::type = 0> self &call(FUNC f) { - return this->call(on_call_t([UVCXX_CAPTURE_MOVE(f)](T ...args) { + return this->call(on_call_t([UVCXX_CAPTURE_MOVE(f)](T ...args) mutable { f(std::forward(args)...); })); } @@ -326,7 +326,7 @@ namespace uvcxx { decltype(std::declval()()), void>::value, int>::type = 0> self &call(FUNC f) { - return this->call(on_call_t([UVCXX_CAPTURE_MOVE(f)](T...) { + return this->call(on_call_t([UVCXX_CAPTURE_MOVE(f)](T...) mutable { f(); })); } @@ -336,7 +336,7 @@ namespace uvcxx { std::declval()(std::declval()))>::value, int>::type = 0> self &except(FUNC f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) mutable -> bool { f(p); return false; })); @@ -346,7 +346,7 @@ namespace uvcxx { std::is_same()())>::value, int>::type = 0> self &except(FUNC f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) mutable -> bool { f(); return false; })); @@ -359,7 +359,7 @@ namespace uvcxx { int>::type = 0> UVCXX_DEPRECATED("specific exception handling functions should return void") self &except(FUNC f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) -> void { (void) f(e); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) mutable -> void { (void) f(e); }); } template::type = 0> UVCXX_DEPRECATED("specific exception handling functions should return void") self &except(FUNC f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) -> void { (void) f(); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) mutable -> void { (void) f(); }); } templateexcept([UVCXX_CAPTURE_MOVE(f)](const E &e) -> void { (void) f(e); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) mutable -> void { (void) f(e); }); } template f) { - m_core->then([UVCXX_CAPTURE_MOVE(f)](void *p) { + m_core->then([UVCXX_CAPTURE_MOVE(f)](void *p) mutable { auto &pack = *(type *) (p); proxy_apply(f, std::move(pack)); }); @@ -266,7 +266,7 @@ namespace uvcxx { std::is_base_of::value, int>::type = 0> self &except(std::function f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) mutable -> bool { try { std::rethrow_exception(p); } catch (const E &e) { @@ -282,7 +282,7 @@ namespace uvcxx { std::is_base_of::value, int>::type = 0> self &except(std::function f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) -> void { f(); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) mutable -> void { f(); }); } self &except(std::function f) { @@ -290,7 +290,7 @@ namespace uvcxx { } self &except(std::function f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) mutable -> bool { return f(); })); } @@ -316,7 +316,7 @@ namespace uvcxx { decltype(std::declval()(std::declval()...)), void>::value, int>::type = 0> self &then(FUNC f) { - return this->then(on_then_t([UVCXX_CAPTURE_MOVE(f)](T ...args) { + return this->then(on_then_t([UVCXX_CAPTURE_MOVE(f)](T ...args) mutable { f(std::forward(args)...); })); } @@ -326,7 +326,7 @@ namespace uvcxx { decltype(std::declval()()), void>::value, int>::type = 0> self &then(FUNC f) { - return this->then(on_then_t([UVCXX_CAPTURE_MOVE(f)](T...) { + return this->then(on_then_t([UVCXX_CAPTURE_MOVE(f)](T...) mutable { f(); })); } @@ -336,7 +336,7 @@ namespace uvcxx { std::declval()(std::declval()))>::value, int>::type = 0> self &except(FUNC f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &p) mutable -> bool { f(p); return false; })); @@ -346,7 +346,7 @@ namespace uvcxx { std::is_same()())>::value, int>::type = 0> self &except(FUNC f) { - return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) -> bool { + return this->except(on_except_t([UVCXX_CAPTURE_MOVE(f)](const std::exception_ptr &) mutable -> bool { f(); return false; })); @@ -359,7 +359,7 @@ namespace uvcxx { int>::type = 0> UVCXX_DEPRECATED("specific exception handling functions should return void") self &except(FUNC f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) -> void { (void) f(e); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) mutable -> void { (void) f(e); }); } template::type = 0> UVCXX_DEPRECATED("specific exception handling functions should return void") self &except(FUNC f) { - return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) -> void { (void) f(); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &) mutable -> void { (void) f(); }); } templateexcept([UVCXX_CAPTURE_MOVE(f)](const E &e) -> void { (void) f(e); }); + return this->except([UVCXX_CAPTURE_MOVE(f)](const E &e) mutable -> void { (void) f(e); }); } template