diff --git a/CHANGELOG.md b/CHANGELOG.md index 877bd26..97967c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ -------------------------------- +## v0.1.3 + +> Since: 2024-07-02 + +### 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. +- Add examples. + +## Bug fix + +- Fix the variable usage error in the `lib_t::open(string)` method. + +## Break changes + +- Change return value of `signal_t::start_oneshot`. + - `callback<int>` -> `promise<int>` + +-------------------------------- + ## v0.1.2 > Date: 2024-07-01 @@ -25,7 +49,7 @@ -------------------------------- -## v0.1.2 +## v0.1.1 > Date: 2024-06-28 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c568a1..b6d01ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.9) project(libuvcxx) +# ------------------------------------------------------------------------------- +# Options +# ------------------------------------------------------------------------------- + # default using std=c++17 as libuvcxx has compatibility to C++11 set(STD "17" CACHE STRING "Set CMAKE_CXX_STANDARD") @@ -9,7 +13,10 @@ option(WERROR "If treat all warning as error" OFF) set(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/bin") +# ------------------------------------------------------------------------------- # Load libuv from submodule or system +# ------------------------------------------------------------------------------- + if (EXISTS "${PROJECT_SOURCE_DIR}/libuv/CMakeLists.txt") # compile static libuv option(LIBUV_BUILD_SHARED "" OFF) @@ -50,8 +57,9 @@ else () message(STATUS "Using system libuv") endif () -include_directories("${PROJECT_SOURCE_DIR}/include") -file(GLOB_RECURSE HEADERS "${PROJECT_SOURCE_DIR}/include/*.h") +# ------------------------------------------------------------------------------- +# Message configuration items +# ------------------------------------------------------------------------------- if (STD) message(STATUS "Using --std=c++${STD}") @@ -66,9 +74,54 @@ if (WERROR) message(STATUS "Enabled -Werror") endif () -# build examples +# ------------------------------------------------------------------------------- +# Collect header files +# ------------------------------------------------------------------------------- + +include_directories("${PROJECT_SOURCE_DIR}/include") +file(GLOB_RECURSE HEADERS "${PROJECT_SOURCE_DIR}/include/uvcxx/*.h") + +# ------------------------------------------------------------------------------- +# Generate single header +# ------------------------------------------------------------------------------- + +find_package(Python 3 COMPONENTS Interpreter) + +set(ENTRY_HEADER "${PROJECT_SOURCE_DIR}/include/uvcxx.h") +set(SINGLE_HEADER "${PROJECT_SOURCE_DIR}/include/uvcxx-single.h") + +if (Python_FOUND) + if ("${Python_VERSION_MAJOR}" GREATER 3 + OR ("${Python_VERSION_MAJOR}" EQUAL 3 AND "${Python_VERSION_MINOR}" GREATER_EQUAL 6)) + message(STATUS "Generating the Single-Header") + # Use python3 generate single header + add_custom_command(OUTPUT "${SINGLE_HEADER}" + COMMAND ${Python_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/merge.py" + DEPENDS ${HEADERS} + COMMENT "python3 scripts/merge.py" + PRE_BUILD # generate single header before any thing + VERBATIM + ) + set_source_files_properties("${SINGLE_HEADER}" PROPERTIES GENERATED TRUE) + add_custom_target(single-header DEPENDS "${SINGLE_HEADER}") + else () + message(WARNING "Requires Python 3.6 but only Python ${Python_VERSION} was found.") + endif () +else () + message(WARNING "Since the Python is not found, the test case for Single-Header will not be generated.") +endif () + +list(APPEND HEADERS "${ENTRY_HEADER}") + +# ------------------------------------------------------------------------------- +# Build examples +# ------------------------------------------------------------------------------- + add_subdirectory(examples) -# add tests +# ------------------------------------------------------------------------------- +# Add tests +# ------------------------------------------------------------------------------- + enable_testing() add_subdirectory(tests) diff --git a/README.md b/README.md index c8466b0..e046631 100644 --- a/README.md +++ b/README.md @@ -64,6 +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. + ## 2. Compatibility `libuvcxx` requires at least `C++11` and is also compatible with the new features in `C++14` and `C++17`. diff --git a/examples/async.cpp b/examples/async.cpp index 50f91a6..76bf59c 100644 --- a/examples/async.cpp +++ b/examples/async.cpp @@ -3,8 +3,22 @@ // L.eval: Let programmer get rid of only work jobs. // +#include <iostream> + #include "uvcxx/async.h" int main() { - return 0; + uv::loop_t loop; + + uv::async_t async; + async.init(loop).call([]() { + std::cout << "async call" << std::endl; + + throw uvcxx::close_handle(); //< close handle after once call + }); + + async.send(); + std::cout << "async send" << std::endl; + + return loop.run(); } diff --git a/examples/barrier.cpp b/examples/barrier.cpp new file mode 100644 index 0000000..c13bbc3 --- /dev/null +++ b/examples/barrier.cpp @@ -0,0 +1,38 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include <iostream> +#include <chrono> + +#include "uvcxx/barrier.h" +#include "uvcxx/thread.h" +#include "uvcxx/utilities.h" + +int main() { + uv::barrier_t barrier(2); + auto beg = std::chrono::steady_clock::now(); + + uv::thread_t thread1([&barrier, beg]() { + std::cout << uvcxx::catstr("thread-1 wait ", 0, "ms\n") << std::flush; + barrier.wait(); + auto end = std::chrono::steady_clock::now(); + auto spent = std::chrono::duration_cast<std::chrono::milliseconds>(end - beg).count(); + std::cout << uvcxx::catstr("thread-1 exit after ", spent, "ms\n") << std::flush; + }); + + uv::thread_t thread2([&barrier, beg]() { + std::cout << uvcxx::catstr("thread-2 wait ", 100, "ms\n") << std::flush; + uv::sleep(100); + barrier.wait(); + auto end = std::chrono::steady_clock::now(); + auto spent = std::chrono::duration_cast<std::chrono::milliseconds>(end - beg).count(); + std::cout << uvcxx::catstr("thread-2 exit after ", spent, "ms\n") << std::flush; + }); + + thread1.join(); + thread2.join(); + + return 0; +} diff --git a/examples/fs.cpp b/examples/fs.cpp index 7536798..4937398 100644 --- a/examples/fs.cpp +++ b/examples/fs.cpp @@ -4,33 +4,73 @@ // #include "uvcxx/fs.h" +#include "uvcxx/buf.h" +#include "uvcxx/cxx/to_string.h" int main() { - std::cout << "----" << std::endl; + std::string content = "!!!!404!!!!"; - uv::fs::open("a.txt", O_CREAT | O_WRONLY, 0777).then([&](int fd) { + // create a.txt and write content into it. + uv::fs::open("a.txt", UV_FS_O_CREAT | UV_FS_O_WRONLY, 0777).then([&](int fd) { std::cout << "open fd = " << fd << std::endl; - uv::fs::write(fd, "!!!!404!!!!", 0).then([fd](ssize_t) { + uv::fs::write(fd, content, 0).then([](ssize_t size) { + std::cout << "write size " << size << std::endl; + }).finally([fd]() { + // close file in finally, whether write succeed or not. (void) uv::fs::close(fd); }); - // throw uvcxx::exception(UV_EAGAIN); - // throw std::logic_error("throw logic after close"); }).except<uvcxx::errcode>([](const uvcxx::errcode &e) { std::cerr << "errcode = " << e.code() << std::endl; - }).except<std::exception>([](const std::exception &e) { - std::cerr << e.what() << std::endl; + exit(-1); }); + uv::default_loop().run(); + + // read a.txt content + uv::fs::open("a.txt", UV_FS_O_RDONLY).then([&](int fd) { + uv::buf_t buf(1024); + uv::fs::read(fd, buf, 0).then([buf](ssize_t size) { + std::cout << "read size " << size << std::endl; + std::cout << "read content: " << std::string(buf.data(), size) << std::endl; + }).finally([fd]() { + // close file in finally, whether write succeed or not. + (void) uv::fs::close(fd); + }); + }); + + uv::default_loop().run(); + { + // call sync version mkdir uv::fs_t tmp; uv::fs::mkdir(nullptr, tmp, "tmp", 0755, nullptr); } - uv::fs::mkdtemp(std::string("tmp") + "/tfolder.XXXXXX").then([](const char *path) { + // make temp folder + uv::fs::mkdtemp(std::string("tmp") + "/folder.XXXXXX").then([](const char *path) { std::cout << "mkdtemp " << path << std::endl; uv::fs_t tmp; uv::fs::rmdir(nullptr, tmp, path, nullptr); }); + // readdir + uv::fs::opendir("tmp").then([](uv_dir_t *dir) { + uv::fs::readdir(dir, 1024).then([](uv_dirent_t *file, size_t size) { + for (decltype(size) i = 0; i < size; ++i) { + std::cout << "<" << uvcxx::to_string(file[i].type) << "> " << file[i].name << std::endl; + } + }).finally([dir]() { + (void) uv::fs::closedir(dir); + }); + }); + + // scandir + uv::fs::scandir("tmp", 0).then([](const uv::fs::scan_next &next) { + uv_dirent_t file{}; + while (next(&file) >= 0) { + std::cout << "<" << uvcxx::to_string(file.type) << "> " << file.name << std::endl; + } + }); + return uv::default_loop().run(); } diff --git a/examples/fs_event.cpp b/examples/fs_event.cpp index 18b0390..9b0908a 100644 --- a/examples/fs_event.cpp +++ b/examples/fs_event.cpp @@ -15,19 +15,25 @@ int main() { { std::ofstream touch(target); } + // setup event watch callback uv::fs_event_t fs_event; fs_event.start(target, UV_FS_EVENT_RECURSIVE).call([](const char *filename, uv_fs_event events) { std::cout << filename << " " << uvcxx::to_string(events) << std::endl; }); + + // change file after 1 second uv::timer_t().start(1 * 1000, 1).call([&]() { std::ofstream touch(target); throw uvcxx::close_handle(); }); + + // close fs_event uv::timer_t().start(2 * 1000, 1).call([&]() { fs_event.close(nullptr); throw uvcxx::close_handle(); }); - // 2 seconds to close + + // wait about 2 seconds before run finished uv::default_loop().run(); return 0; } diff --git a/examples/getaddrinfo.cpp b/examples/getaddrinfo.cpp index 894b7f5..a4e9880 100644 --- a/examples/getaddrinfo.cpp +++ b/examples/getaddrinfo.cpp @@ -5,6 +5,7 @@ #include "uvcxx/getaddrinfo.h" #include "uvcxx/getnameinfo.h" +#include "uvcxx/utilities.h" int main() { addrinfo hints = {}; @@ -16,10 +17,8 @@ int main() { uv::getaddrinfo( "baidu.com", nullptr, &hints ).then([](addrinfo *ai) { - char addr[INET6_ADDRSTRLEN]; for (addrinfo *info = ai; info != nullptr; info = info->ai_next) { - uv_ip4_name((struct sockaddr_in *) info->ai_addr, addr, sizeof(addr)); - std::cout << "Found address: " << addr << std::endl; + std::cout << "Found address: " << uv::ip4_name((struct sockaddr_in *) info->ai_addr) << std::endl; uv::getnameinfo(info->ai_addr, 0).then([](const char *hostname, const char *service) { std::cout << "Name: " << hostname << " " << service << std::endl; }); diff --git a/examples/signal.cpp b/examples/signal.cpp new file mode 100644 index 0000000..4e14a7e --- /dev/null +++ b/examples/signal.cpp @@ -0,0 +1,73 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include <iostream> + +#include "uvcxx/signal.h" +#include "uvcxx/utils/assert.h" + +int loop_handle_count(uv::loop_t &loop) { + int count = 0; + loop.walk([](uv_handle_t *, void *arg) { + (*(int *) arg)++; + }, &count); + return count; +} + +int main(int argc, const char *argv[]) { + int sig = SIGINT; + const char *key = "Ctrl+C"; + + if (argc > 1 && argv[1] == std::string("ide")) { + sig = SIGTERM; + key = "[Stop]"; + } + + std::cout << "Test signal " << sig << " for " << key << std::endl; + + uv::loop_t loop; + + std::cout << "Press " << key << " at least " << 4 << " times." << std::endl; + + int count = 0; + // hold handle to start and stop. + { + uv::signal_t signal(loop); + signal.start(sig).call([&, key](int) mutable { + ++count; + std::cout << "Received " << key << " " << count << std::endl; + signal.stop(); + }); + loop.run(); + (void) signal.start(sig); + loop.run(); + } + uvcxx_assert(loop_handle_count(loop) == 1, "count = ", loop_handle_count(loop)); + loop.run(); + uvcxx_assert(loop_handle_count(loop) == 0, "count = ", loop_handle_count(loop)); + // start backend to watch signal + { + // start backend + uv::signal_t signal(loop); + signal.start(sig).call([key, &count](int) mutable { + ++count; + std::cout << "Received " << key << " " << count << std::endl; + throw uvcxx::close_handle(); + }); + } + loop.run(); + uvcxx_assert(loop_handle_count(loop) == 0, "count = ", loop_handle_count(loop)); + // start oneshot + { + uv::signal_t(loop).start_oneshot(sig).then([key, &count](int) mutable { + ++count; + std::cout << "Received " << key << " " << count << std::endl; + }); + } + loop.run(); + uvcxx_assert(loop_handle_count(loop) == 0); + + return 0; +} diff --git a/include/uvcxx/async.h b/include/uvcxx/async.h index a57dcd5..5094a1a 100644 --- a/include/uvcxx/async.h +++ b/include/uvcxx/async.h @@ -20,15 +20,20 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> init() { - return init(default_loop()); + uvcxx::callback<> callback() { + return get_data<data_t>()->send_cb.callback();; } UVCXX_NODISCARD uvcxx::callback<> init(const loop_t &loop) { UVCXX_APPLY(uv_async_init(loop, *this, raw_callback), nullptr); _detach_(); - return get_data<data_t>()->send_cb.callback(); + return callback(); + } + + UVCXX_NODISCARD + uvcxx::callback<> init() { + return init(default_loop()); } int send() { diff --git a/include/uvcxx/check.h b/include/uvcxx/check.h index 828f198..f7293ca 100644 --- a/include/uvcxx/check.h +++ b/include/uvcxx/check.h @@ -22,11 +22,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<> start() { UVCXX_APPLY(uv_check_start(*this, raw_callback), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } void stop() { diff --git a/include/uvcxx/fs_event.h b/include/uvcxx/fs_event.h index 4d55b67..4b43028 100644 --- a/include/uvcxx/fs_event.h +++ b/include/uvcxx/fs_event.h @@ -28,11 +28,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<const char *, uv_fs_event> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<const char *, uv_fs_event> start(uvcxx::string path, int flags) { UVCXX_APPLY(uv_fs_event_start(*this, raw_callback, path, flags), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } void stop() { diff --git a/include/uvcxx/fs_poll.h b/include/uvcxx/fs_poll.h index e92080c..6825f08 100644 --- a/include/uvcxx/fs_poll.h +++ b/include/uvcxx/fs_poll.h @@ -25,11 +25,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<const uv_stat_t *, const uv_stat_t *> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<const uv_stat_t *, const uv_stat_t *> start(uvcxx::string path, unsigned int interval) { UVCXX_APPLY(uv_fs_poll_start(*this, raw_callback, path, interval), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } void stop() { diff --git a/include/uvcxx/handle.h b/include/uvcxx/handle.h index b739c12..d2a4e42 100644 --- a/include/uvcxx/handle.h +++ b/include/uvcxx/handle.h @@ -105,6 +105,11 @@ namespace uv { #endif + UVCXX_NODISCARD + uvcxx::promise<> close_callback() { + return get_data<data_t>()->close_cb.promise(); + } + void close(std::nullptr_t) { (void) close_for([&](void (*cb)(raw_t *)) { uv_close(*this, cb); diff --git a/include/uvcxx/idle.h b/include/uvcxx/idle.h index c4a11aa..99bdbf1 100644 --- a/include/uvcxx/idle.h +++ b/include/uvcxx/idle.h @@ -22,11 +22,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<> start() { UVCXX_APPLY(uv_idle_start(*this, raw_callback), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } void stop() { diff --git a/include/uvcxx/lib.h b/include/uvcxx/lib.h index be47ff7..dba789a 100644 --- a/include/uvcxx/lib.h +++ b/include/uvcxx/lib.h @@ -15,6 +15,7 @@ #include "cxx/string.h" #include "cxx/wrapper.h" #include "inner/base.h" +#include "utils/defer.h" namespace uv { /** @@ -47,7 +48,7 @@ namespace uv { auto lib = new uv_lib_t; uvcxx::defer_delete<uv_lib_t> delete_lib(lib); - UVCXX_APPLY(uv_dlopen(filename, *this), status, "can not open `", filename, "`"); + UVCXX_APPLY(uv_dlopen(filename, lib), status, "can not open `", filename, "`"); delete_lib.release(); (void) reset_raw(lib, [](uv_lib_t *pre) { diff --git a/include/uvcxx/poll.h b/include/uvcxx/poll.h index d128b95..3da3264 100644 --- a/include/uvcxx/poll.h +++ b/include/uvcxx/poll.h @@ -45,6 +45,11 @@ namespace uv { return init_socket(default_loop(), socket); } + UVCXX_NODISCARD + uvcxx::callback<int> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<int> start(int events) { auto data = get_data<data_t>(); diff --git a/include/uvcxx/prepare.h b/include/uvcxx/prepare.h index cf8a0dd..3a8b9be 100644 --- a/include/uvcxx/prepare.h +++ b/include/uvcxx/prepare.h @@ -22,11 +22,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<> start() { UVCXX_APPLY(uv_prepare_start(*this, raw_callback), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } void stop() { diff --git a/include/uvcxx/process.h b/include/uvcxx/process.h index be8422f..86b1d31 100644 --- a/include/uvcxx/process.h +++ b/include/uvcxx/process.h @@ -242,6 +242,7 @@ namespace uv { return raw<raw_t>()->pid; } + UVCXX_NODISCARD uvcxx::promise<int64_t, int> spawn(const loop_t &loop, const uv_process_options_t *options) { auto fix_options = *options; fix_options.exit_cb = raw_exit_callback; @@ -252,6 +253,7 @@ namespace uv { return get_data<data_t>()->exit_cb.promise(); } + UVCXX_NODISCARD uvcxx::promise<int64_t, int> spawn(const uv_process_options_t *options) { return this->spawn(default_loop(), options); } diff --git a/include/uvcxx/signal.h b/include/uvcxx/signal.h index 4e63346..37a94c7 100644 --- a/include/uvcxx/signal.h +++ b/include/uvcxx/signal.h @@ -27,26 +27,30 @@ namespace uv { return raw<raw_t>()->signum; } + UVCXX_NODISCARD + uvcxx::callback<int> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<int> start(int signum) { UVCXX_APPLY(uv_signal_start(*this, raw_callback, signum), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } #if UVCXX_SATISFY_VERSION(1, 12, 0) UVCXX_NODISCARD - uvcxx::callback<int> start_oneshot(int signum) { - UVCXX_APPLY(uv_signal_start_oneshot(*this, raw_callback, signum), nullptr); + uvcxx::promise<int> start_oneshot(int signum) { + UVCXX_APPLY(uv_signal_start_oneshot(*this, raw_oneshot_callback, signum), nullptr); _detach_(); auto attachment = *this; - return get_data<data_t>()->start_cb.callback().finally(nullptr) - .finally([attachment]() mutable { - finally_recycle_oneshot(attachment); - }); + return get_data<data_t>()->start_oneshot_cb.promise().finally([attachment]() mutable { + finally_recycle_oneshot(attachment); + }); } #endif @@ -82,10 +86,17 @@ namespace uv { auto data = (data_t *) (handle->data); data->start_cb.emit(signum); } + static void raw_oneshot_callback(raw_t *handle, int signum) { + auto data = (data_t *) (handle->data); + data->start_oneshot_cb.resolve(signum); + data->start_oneshot_cb.finalize(); + data->start_oneshot_cb.promise().then(nullptr).except(nullptr).finally(nullptr); + } class data_t : supper::data_t { public: uvcxx::callback_emitter<int> start_cb; + uvcxx::promise_emitter<int> start_oneshot_cb; explicit data_t(signal_t &handle) : supper::data_t(handle) { diff --git a/include/uvcxx/stream.h b/include/uvcxx/stream.h index d599f33..54f92ef 100644 --- a/include/uvcxx/stream.h +++ b/include/uvcxx/stream.h @@ -34,6 +34,11 @@ namespace uv { return ::uv::shutdown(*this); } + UVCXX_NODISCARD + uvcxx::callback<> listen_callback() { + return get_data<data_t>()->listen_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<> listen(int backlog) { auto data = get_data<data_t>(); @@ -52,13 +57,29 @@ namespace uv { UVCXX_PROXY(uv_accept(*this, client)); } + UVCXX_NODISCARD + uvcxx::callback<size_t, uv_buf_t *> alloc_callback() { + return get_data<data_t>()->alloc_cb.callback(); + } + + /** + * Different from `alloc_callback` which only acquires the callback function, + * this method will clear the previously registered callback to avoid repeatedly `allocate`. + * @return + */ UVCXX_NODISCARD uvcxx::callback<size_t, uv_buf_t *> alloc() { // this alloc is not under Running state, so no `_detach_` applied. // memory alloc can not register multi-times callback + // use call(nullptr) to avoid call alloc multi times. return get_data<data_t>()->alloc_cb.callback().call(nullptr); } + UVCXX_NODISCARD + uvcxx::callback<ssize_t, const uv_buf_t *> read_callback() { + return get_data<data_t>()->read_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<ssize_t, const uv_buf_t *> read_start() { auto data = get_data<data_t>(); @@ -282,9 +303,15 @@ namespace uv { virtual stream_t accept() = 0; + UVCXX_NODISCARD + uvcxx::callback<stream_t> accept_callback() { + return data<data_t>()->accept_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<stream_t> listen_accept(int backlog) { auto data = this->data<data_t>(); + this->listen(backlog).call(nullptr).call([data, this]() { data->accept_cb.emit(this->accept()); }).except(nullptr).except([data](const std::exception_ptr &p) -> bool { diff --git a/include/uvcxx/timer.h b/include/uvcxx/timer.h index 7e9ad58..3bceec9 100644 --- a/include/uvcxx/timer.h +++ b/include/uvcxx/timer.h @@ -22,11 +22,16 @@ namespace uv { _attach_close_(); } + UVCXX_NODISCARD + uvcxx::callback<> callback() { + return get_data<data_t>()->start_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<> start(uint64_t timeout, uint64_t repeat) { UVCXX_APPLY(uv_timer_start(*this, raw_callback, timeout, repeat), nullptr); _detach_(); - return get_data<data_t>()->start_cb.callback(); + return callback(); } int stop() { diff --git a/include/uvcxx/udp.h b/include/uvcxx/udp.h index 2d9a125..9ffea70 100644 --- a/include/uvcxx/udp.h +++ b/include/uvcxx/udp.h @@ -171,19 +171,35 @@ namespace uv { return try_send(buffers.data(), (unsigned int) buffers.size(), addr); } + UVCXX_NODISCARD + uvcxx::callback<size_t, uv_buf_t *> alloc_callback() { + return get_data<data_t>()->alloc_cb.callback(); + } + + /** + * Different from `alloc_callback` which only acquires the callback function, + * this method will clear the previously registered callback to avoid repeatedly `allocate`. + * @return + */ UVCXX_NODISCARD uvcxx::callback<size_t, uv_buf_t *> alloc() { // this alloc is not under Running state, so no `_detach_` applied. // memory alloc can not register multi-times callback + // use call(nullptr) to avoid call alloc multi times. return get_data<data_t>()->alloc_cb.callback().call(nullptr); } + UVCXX_NODISCARD + uvcxx::callback<ssize_t, const uv_buf_t *, const sockaddr *, unsigned> recv_callback() { + return get_data<data_t>()->recv_cb.callback(); + } + UVCXX_NODISCARD uvcxx::callback<ssize_t, const uv_buf_t *, const sockaddr *, unsigned> recv_start() { UVCXX_APPLY(uv_udp_recv_start(*this, raw_alloc_callback, raw_recv_callback), nullptr); _detach_(); - return get_data<data_t>()->recv_cb.callback(); + return recv_callback(); } #if UVCXX_SATISFY_VERSION(1, 39, 0) diff --git a/include/uvcxx/utilities.h b/include/uvcxx/utilities.h index fa84983..b6243e2 100644 --- a/include/uvcxx/utilities.h +++ b/include/uvcxx/utilities.h @@ -221,16 +221,34 @@ namespace uv { UVCXX_PROXY(uv_ip4_name(src, dst, size)); } + inline std::string ip4_name(const sockaddr_in *src) { + char dst[UV_IF_NAMESIZE] = {0}; + (void) ip4_name(src, dst, sizeof(dst)); + return dst; + } + inline int ip6_name(const sockaddr_in6 *src, char *dst, size_t size) { UVCXX_PROXY(uv_ip6_name(src, dst, size)); } + inline std::string ip6_name(const sockaddr_in6 *src) { + char dst[UV_IF_NAMESIZE] = {0}; + (void) ip6_name(src, dst, sizeof(dst)); + return dst; + } + #if UVCXX_SATISFY_VERSION(1, 43, 0) inline int ip_name(const sockaddr *src, char *dst, size_t size) { UVCXX_PROXY(uv_ip_name(src, dst, size)); } + inline std::string ip_name(const sockaddr *src) { + char dst[UV_IF_NAMESIZE] = {0}; + (void) ip_name(src, dst, sizeof(dst)); + return dst; + } + #endif inline int inet_ntop(int af, uvcxx::string src, char *dst, size_t size) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e07f0b2..a783f48 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -45,6 +45,11 @@ foreach (path ${FILES}) endif () string(REGEX MATCH "^[^.]*" file "${file_ext}") + # skip single header if there is no that target + if (NOT TARGET single-header AND "${PREFIX}-${file}" STREQUAL "test-single-header") + continue() + endif () + # check if target exists list(FIND TARGETS "${PREFIX}-${file}" TARGET_INDEX) if (TARGET_INDEX GREATER -1) @@ -59,6 +64,11 @@ foreach (path ${FILES}) endif () endforeach () +# explicitly add test-single-header target dependencies +if (TARGET single-header) + add_dependencies(test-single-header single-header) +endif () + if (WALL) if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") foreach (target ${TARGETS}) diff --git a/tests/single-header.cpp b/tests/single-header.cpp new file mode 100644 index 0000000..13ecc4c --- /dev/null +++ b/tests/single-header.cpp @@ -0,0 +1,13 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#include <iostream> + +#include "uvcxx-single.h" + +int main() { + std::cout << "Generation succeeded." << std::endl; + return 0; +}