diff --git a/src/uvw/check.hpp b/src/uvw/check.hpp index 83ab02e1..8b66bf31 100644 --- a/src/uvw/check.hpp +++ b/src/uvw/check.hpp @@ -1,6 +1,8 @@ #pragma once +#include +#include #include #include "resource.hpp" #include "error.hpp" @@ -10,31 +12,43 @@ namespace uvw { class Check final: public Resource { - static void proto(uv_check_t* h) { - static_cast(h->data)->callback(UVWError{}); + static Check* startCallback(uv_check_t* h) { + Check *check = static_cast(h->data); + check->startCb(UVWError{}); + return check; + } + + explicit Check(std::shared_ptr ref) + : Resource{HandleType{}, std::move(ref)} + { + initialized = (uv_check_init(parent(), get()) == 0); } public: - using Callback = std::function; - - explicit Check(uv_loop_t *loop): Resource{&handle} { - uv_check_init(loop, &handle); + template + static std::shared_ptr create(Args&&... args) { + return std::shared_ptr{new Check{std::forward(args)...}}; } - void start(Callback cb) noexcept { - callback = cb; - auto err = uv_check_start(&handle, &proto); + void start(std::function cb) noexcept { + using UVCB = UVCallback; + auto func = UVCB::get(this); + startCb = std::move(cb); + auto err = uv_check_start(get(), func); if(err) { - callback(UVWError{err}); + startCb(UVWError{err}); + reset(); } } - bool stop() noexcept { return (uv_check_stop(&handle) == 0); } + UVWError stop() noexcept { return UVWError{uv_check_stop(get())}; } + + explicit operator bool() { return initialized; } private: - uv_check_t handle; - Callback callback; + std::function startCb; + bool initialized; }; diff --git a/src/uvw/error.hpp b/src/uvw/error.hpp index d28d6753..6ced2e63 100644 --- a/src/uvw/error.hpp +++ b/src/uvw/error.hpp @@ -33,7 +33,8 @@ public: public: explicit operator bool() const noexcept { return !(ec == 0); } - const char* str() const noexcept { return uv_strerror(ec); } + operator const char *() const noexcept { return uv_strerror(ec); } + operator int() const noexcept { return ec; } private: int ec; diff --git a/src/uvw/loop.hpp b/src/uvw/loop.hpp index db1a1e29..3e8fa76d 100644 --- a/src/uvw/loop.hpp +++ b/src/uvw/loop.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "error.hpp" @@ -11,47 +12,90 @@ namespace uvw { +class BaseResource; class Loop; template class Handle { + template + friend class Handle; + friend class Loop; template - explicit constexpr Handle(Args&&... args) - : res{std::make_shared(std::forward(args)...)} + explicit constexpr Handle(std::shared_ptr&& l, Args&&... args) + : res{R::create(std::move(l), std::forward(args)...)} { } -public: - constexpr Handle(const Handle &other): res{other.res} { } - constexpr Handle(Handle &&other): res{std::move(other.res)} { } + explicit constexpr Handle(std::shared_ptr ptr): res{std::move(ptr)} { } - constexpr void operator=(const Handle &other) { res = other.res; } - constexpr void operator=(Handle &&other) { res = std::move(other.res); } +public: + explicit constexpr Handle(): res{} { } + + template::value>* = nullptr> + constexpr Handle(const Handle &other): res{other.res} { } + + template::value>* = nullptr> + constexpr Handle(Handle &&other): res{std::move(other.res)} { } + + template::value>* = nullptr> + constexpr void operator=(const Handle &other) { res = other.res; } + + template::value>* = nullptr> + constexpr void operator=(Handle &&other) { res = std::move(other.res); } + + constexpr explicit operator bool() const { return static_cast(res); } constexpr operator R&() noexcept { return *res; } - operator const R&() const noexcept { return *res; } + constexpr operator const R&() const noexcept { return *res; } + + template::value>* = nullptr> + constexpr operator Handle() { return Handle{res}; } private: std::shared_ptr res; }; -class Loop final { -public: - Loop(bool def = true) - : loop{def ? uv_default_loop() : new uv_loop_t, [def](uv_loop_t *l){ if(!def) delete l; }} - { - if(!def) { - auto err = uv_loop_init(loop.get()); +class Loop final: public std::enable_shared_from_this { + template + friend class Resource; - if(err) { - throw UVWException{err}; - } - } else if(!loop) { - throw std::bad_alloc{}; + using Deleter = std::function; + + Loop(std::unique_ptr ptr): loop{std::move(ptr)} { } + +public: + static std::shared_ptr create() { + auto ptr = std::unique_ptr{new uv_loop_t, [](uv_loop_t *l){ delete l; }}; + auto loop = std::shared_ptr(new Loop{std::move(ptr)}); + + if(uv_loop_init(loop->loop.get())) { + loop = nullptr; } + + return loop; + } + + static std::shared_ptr getDefault() { + static std::weak_ptr ref; + std::shared_ptr loop; + + if(ref.expired()) { + auto def = uv_default_loop(); + + if(def) { + auto ptr = std::unique_ptr(def, [](uv_loop_t *){ }); + loop = std::shared_ptr(new Loop{std::move(ptr)}); + } + + ref = loop; + } else { + loop = ref.lock(); + } + + return loop; } Loop(const Loop &) = delete; @@ -67,11 +111,11 @@ public: template Handle handle(Args&&... args) { - return Handle{loop.get(), std::forward(args)...}; + return Handle{shared_from_this(), std::forward(args)...}; } - bool close() noexcept { - return (uv_loop_close(loop.get()) == 0); + UVWError close() noexcept { + return UVWError{uv_loop_close(loop.get())}; } bool run() noexcept { @@ -95,7 +139,6 @@ public: } private: - using Deleter = std::function; std::unique_ptr loop; }; diff --git a/src/uvw/resource.hpp b/src/uvw/resource.hpp index dda714c3..f3f7e2c5 100644 --- a/src/uvw/resource.hpp +++ b/src/uvw/resource.hpp @@ -11,49 +11,98 @@ namespace uvw { +template +struct HandleType { }; + + +template +class Resource; + + +template +struct UVCallback; + + +template +struct UVCallback { + template + static auto get(T *res); + +private: + template + static void proto(Args... args); +}; + + template -class Resource { +class Resource: public std::enable_shared_from_this { + template + friend struct UVCallback; + + static Resource* closeCallback(uv_handle_t* h) { + auto ptr = static_cast*>(h->data); + ptr->closeCb(UVWError{}); + return ptr; + } + protected: template - explicit Resource(U *u): handle{reinterpret_cast(u)} { - handle->data = this; - } + explicit Resource(HandleType, std::shared_ptr r) + : pLoop{std::move(r)}, handle{std::make_shared()} + { } - static void proto(uv_handle_t* h) { - static_cast(h->data)->callback(UVWError{}); - } + template + U* get() const noexcept { return reinterpret_cast(handle.get()); } + + uv_loop_t* parent() const noexcept { return pLoop->loop.get(); } + void reset() noexcept { ref = nullptr; } public: - using Callback = std::function; + explicit Resource(const Resource &) = delete; + explicit Resource(Resource &&) = delete; - virtual ~Resource() = 0; - - Resource(const Resource &) = delete; - Resource(Resource &&) = delete; + ~Resource() { static_assert(std::is_base_of, T>::value, "!"); } void operator=(const Resource &) = delete; void operator=(Resource &&) = delete; - bool active() const noexcept { return !(uv_is_active(handle) == 0); } - bool closing() const noexcept { return !(uv_is_closing(handle) == 0); } + std::shared_ptr loop() const noexcept { return pLoop; } - void close(Callback cb) noexcept { - callback = cb; - uv_close(handle, &proto); + bool active() const noexcept { return !(uv_is_active(get()) == 0); } + bool closing() const noexcept { return !(uv_is_closing(get()) == 0); } + + void reference() noexcept { uv_ref(get()); } + void unreference() noexcept { uv_ref(get()); } + bool referenced() const noexcept { return !(uv_has_ref(get()) == 0); } + + void close(std::function cb) noexcept { + using UVCB = UVCallback; + auto func = UVCB::get, &closeCallback>(this); + closeCb = std::move(cb); + uv_close(get(), func); } - void reference() noexcept { uv_ref(handle); } - void unreference() noexcept { uv_ref(handle); } - bool referenced() const noexcept { return !(uv_has_ref(handle) == 0); } - private: - uv_handle_t *handle; - Callback callback; + std::function closeCb; + std::shared_ptr pLoop; + std::shared_ptr handle; + std::shared_ptr ref; }; -template -Resource::~Resource() { - static_assert(std::is_base_of, T>::value, "!"); + +template +template +void UVCallback::proto(Args... args) { + T *res = F(std::forward(args)...); + res->ref = nullptr; +} + +template +template +auto UVCallback::get(T *res) { + res->template get()->data = res; + res->ref = res->shared_from_this(); + return &proto; } diff --git a/src/uvw/stream.hpp b/src/uvw/stream.hpp index 02d48f1f..01b11648 100644 --- a/src/uvw/stream.hpp +++ b/src/uvw/stream.hpp @@ -1,18 +1,71 @@ #pragma once +#include +#include "resource.hpp" +#include "loop.hpp" + + namespace uvw { -template -class Connection: public Resource { +template +class Stream: public Resource { + static void protoListen(uv_stream_t* srv, int status) { + Stream &stream = *(static_cast(srv->data)); + + if(status) { + stream.listenCallback(UVWError{status}); + } else { + stream.tryAccept(); + } + } + + void tryAccept() const noexcept { + Handle handle = accept(); + + if(handle) { + // TODO invoke cb with handle + } else { + // TODO invoke cb with error + } + } + + virtual Handle accept() const noexcept = 0; + +protected: using Resource::Resource; -}; +public: + using CallbackListen = std::function; -template -class Stream: public Connection { - using Connection::Connection; + // TODO shutdown + + void listen(int backlog, CallbackListen cb) noexcept { + listenCallback = std::move(cb); + this->template get()->data = this; + auto err = uv_listen(this->template get(), backlog, &protoListen); + + if(err) { + listenCallback(UVWError{err}); + } + } + + // TODO read + // TODO stop + // TODO write + // TODO tryWrite + + bool readable() const noexcept { + return (uv_is_readable(this->template get()) == 1); + } + + bool writable() const noexcept { + return (uv_is_writable(this->template get()) == 1); + } + +private: + CallbackListen listenCallback; }; diff --git a/src/uvw/tcp.hpp b/src/uvw/tcp.hpp index 6da6867e..f09b53e3 100644 --- a/src/uvw/tcp.hpp +++ b/src/uvw/tcp.hpp @@ -1,7 +1,10 @@ #pragma once +#include #include +#include +#include #include #include #include @@ -14,9 +17,30 @@ namespace uvw { class Tcp final: public Stream { static void protoConnect(uv_connect_t* req, int status) { - auto handle = req->handle; - delete req; - static_cast(handle->data)->connCb(UVWError{status}); + Tcp *tcp = static_cast(req->handle->data); + tcp->connCb(UVWError{status}); + tcp->connCb = nullptr; + } + + Handle accept() const noexcept override { + auto handle = loop()->handle(get()); + + if(!handle || !static_cast(handle)) { + handle = Handle{}; + } + + return handle; + } + + explicit Tcp(std::shared_ptr ref) + : Stream{HandleType{}, std::move(ref)}, + conn{std::make_unique()} + { + initialized = (uv_tcp_init(parent(), get()) == 0); + } + + explicit Tcp(std::shared_ptr ref, uv_stream_t *srv): Tcp{ref} { + initialized = initialized || (uv_accept(srv, get()) == 0); } public: @@ -25,51 +49,52 @@ public: enum { IPv4, IPv6 }; - explicit Tcp(uv_loop_t *loop): Stream{&handle} { - uv_tcp_init(loop, &handle); + template + static std::shared_ptr create(Args&&... args) { + return std::shared_ptr{new Tcp{std::forward(args)...}}; } - ~Tcp() { - close([](UVWError){}); + UVWError noDelay(bool value = false) noexcept { + return UVWError{uv_tcp_nodelay(get(), value ? 1 : 0)}; } - bool noDelay(bool value = false) { - return (uv_tcp_nodelay(&handle, value ? 1 : 0) == 0); - } - - bool keepAlive(bool enable = false, Time time = Time{0}) { - return (uv_tcp_keepalive(&handle, enable ? 1 : 0, time.count()) == 0); + UVWError keepAlive(bool enable = false, Time time = Time{0}) noexcept { + return UVWError{uv_tcp_keepalive(get(), enable ? 1 : 0, time.count())}; } template void connect(std::string, int, CallbackConnect) noexcept; + explicit operator bool() { return initialized; } + private: + std::unique_ptr conn; CallbackConnect connCb; - uv_tcp_t handle; + bool initialized; }; template<> void Tcp::connect(std::string ip, int port, CallbackConnect cb) noexcept { - uv_connect_t *conn = new uv_connect_t; sockaddr_in addr; uv_ip4_addr(ip.c_str(), port, &addr); - connCb = cb; - auto err = uv_tcp_connect(conn, &handle, reinterpret_cast(&addr), &protoConnect); + connCb = std::move(cb); + get()->data = this; + auto err = uv_tcp_connect(conn.get(), get(), reinterpret_cast(&addr), &protoConnect); if(err) { connCb(UVWError{err}); } } + template<> void Tcp::connect(std::string ip, int port, CallbackConnect cb) noexcept { - uv_connect_t *conn = new uv_connect_t; sockaddr_in6 addr; uv_ip6_addr(ip.c_str(), port, &addr); - connCb = cb; - auto err = uv_tcp_connect(conn, &handle, reinterpret_cast(&addr), &protoConnect); + connCb = std::move(cb); + get()->data = this; + auto err = uv_tcp_connect(conn.get(), get(), reinterpret_cast(&addr), &protoConnect); if(err) { connCb(UVWError{err}); diff --git a/src/uvw/timer.hpp b/src/uvw/timer.hpp index b476b9af..353fcf8d 100644 --- a/src/uvw/timer.hpp +++ b/src/uvw/timer.hpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -13,35 +14,48 @@ namespace uvw { class Timer final: public Resource { - static void proto(uv_timer_t* h) { - static_cast(h->data)->callback(UVWError{}); + static Timer* startCallback(uv_timer_t* h) { + Timer *timer = static_cast(h->data); + timer->startCb(UVWError{}); + return timer; + } + + explicit Timer(std::shared_ptr ref) + : Resource{HandleType{}, std::move(ref)} + { + initialized = (uv_timer_init(parent(), get()) == 0); } public: using Time = std::chrono::duration; - using Callback = std::function; - explicit Timer(uv_loop_t *loop): Resource{&handle} { - uv_timer_init(loop, &handle); + template + static std::shared_ptr create(Args&&... args) { + return std::shared_ptr{new Timer{std::forward(args)...}}; } - void start(const Time &timeout, const Time &rep, Callback cb) noexcept { - callback = cb; - auto err = uv_timer_start(&handle, &proto, timeout.count(), rep.count()); + void start(const Time &timeout, const Time &rep, std::function cb) noexcept { + using UVCB = UVCallback; + auto func = UVCB::get(this); + startCb = std::move(cb); + auto err = uv_timer_start(get(), func, timeout.count(), rep.count()); if(err) { - callback(UVWError{err}); + startCb(UVWError{err}); + reset(); } } - void stop() noexcept { uv_timer_stop(&handle); } - void again() noexcept { uv_timer_again(&handle); } - void repeat(const Time &rep) noexcept { uv_timer_set_repeat(&handle, rep.count()); } - Time repeat() const noexcept { return Time{uv_timer_get_repeat(&handle)}; } + UVWError stop() noexcept { return UVWError{uv_timer_stop(get())}; } + UVWError again() noexcept { return UVWError{uv_timer_again(get())}; } + void repeat(const Time &rep) noexcept { uv_timer_set_repeat(get(), rep.count()); } + Time repeat() const noexcept { return Time{uv_timer_get_repeat(get())}; } + + explicit operator bool() { return initialized; } private: - uv_timer_t handle; - Callback callback; + std::function startCb; + bool initialized; }; diff --git a/test/main.cpp b/test/main.cpp index d83b2a38..cc86ad7e 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -4,14 +4,26 @@ void f(uvw::Loop &loop) { uvw::Handle handle = loop.handle(); - auto cb = [h{handle}](uvw::UVWError err){ std::cout << "---" << ((bool)err) << std::endl; }; + + auto cb = [handle](uvw::UVWError err) mutable { + std::cout << "---" << ((bool)err) << std::endl; + uvw::Tcp &tcp = handle; + tcp.close([](uvw::UVWError err) mutable { + std::cout << "---" << ((bool)err) << std::endl; + }); + }; + uvw::Tcp &tcp = handle; tcp.connect(std::string{"127.0.0.1"}, 80, cb); } +void g() { + auto loop = uvw::Loop::getDefault(); + f(*loop); + loop->run(); + loop = nullptr; +} int main() { - uvw::Loop loop; - f(loop); - loop.runWait(); + g(); }