From 71fd97ca2f449998883ea3c15c82e2545a29a7ed Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 20 Jul 2026 18:49:46 +0300 Subject: [PATCH] ... --- include/snipplib/network/snplib_network.h | 174 ++++++++++++++++++++-- 1 file changed, 162 insertions(+), 12 deletions(-) diff --git a/include/snipplib/network/snplib_network.h b/include/snipplib/network/snplib_network.h index cd68793..1be488f 100644 --- a/include/snipplib/network/snplib_network.h +++ b/include/snipplib/network/snplib_network.h @@ -1,7 +1,9 @@ #pragma once #include + #include "../concepts/snplib_concepts.h" +#include "../utils/snplib_logger.h" namespace snplib { @@ -53,18 +55,26 @@ concept snplib_net_connection_c = requires(T t) { template -concept snplib_net_session_c = std::derived_from> && requires(T t) { - // static constexpr member - std::formattable; - []() { [[maybe_unused]] static constexpr auto val = T::id; }(); +concept snplib_net_session_c = + std::derived_from> && + requires(T t, const T t_const) { + typename T::ident_t; + typename T::context_t; - snplib_net_connection_c; + snplib_net_connection_c; - // { t.startSession(std::declval()) } -> snplib_async_retval_c; - t.startSession(std::declval()); + { t_const.ident() } -> std::same_as; - t.stopSession(); -}; + // { t.startSession(std::declval()) } -> snplib_async_retval_c; + t.startSession(std::declval()); + + t.stopSession(); + } && (!std::is_void_v || std::constructible_from) && + (std::is_void_v || + (std::constructible_from || + std::constructible_from)); // can be constructible from value or + // lvalue-reference or const + // lvalue-reference of context_t-type template @@ -83,6 +93,45 @@ concept snplib_net_listener_c = requires(T t) { }; +// a type that defines at least two compile-time constants: +// ROLE_SERVER and ROLE_CLIENT +template +concept snplib_net_initiator_role_c = requires { + []() { + [[maybe_unused]] static constexpr std::array arr{// server role (listener of connections) + T::ROLE_SERVER, + // cleant role (opener of connections) + T::ROLE_CLIENT}; + }(); +}; + +// default library-wide type of network connection initiator role +enum class snplib_net_initiator_role_e : int { ROLE_SERVER, ROLE_CLIENT }; + + +template +concept snplib_net_initiator_c = + requires(T t) { + // common part + requires snplib_net_initiator_role_c; + []() { [[maybe_unused]] static constexpr auto val = T::ROLE; }(); + + snplib_net_connection_c; + } && ((T::ROLE != decltype(T::ROLE)::ROLE_SERVER) || requires(T t) { // use of short-circuiting logic!!! + // server-role + { t.accept() } -> snplib_async_retval_c; + + { t.startListen() } -> snplib_error_c; + + t.stopListen(); + }) && ((T::ROLE != decltype(T::ROLE)::ROLE_CLIENT) || requires(T t) { // use of short-circuiting logic!!! + // client-role + { t.open() } -> snplib_async_retval_c; + t.close(); + }); + + + template concept snplib_netserver_c = requires(T t) { // static constexpr member @@ -104,17 +153,118 @@ concept snplib_netserver_c = requires(T t) { }; +template +concept snplib_session_manager_c = + requires(T t, SessionT::context_t cnx) { + requires snplib_net_session_c; + + typename T::session_ptr_t; // e.g. std::shared_ptr + + // must be a non-blocking method! + t.startSession(std::declval()); + + t.stopSession(std::declval()); + + // must return number of stopped sessions + { t.stopSessions() } -> std::convertible_to; + + // true - if session exists + { t.isActive(std::declval()) } -> std::convertible_to; + } && (!std::is_void_v || requires(T t) { + { t.createSession() } -> std::same_as; + }) && (std::is_void_v || requires(T t, SessionT::context_t cnx) { + { t.createSession(cnx) } -> std::same_as; + }); +; + /* SINGLE-PROTO*/ -template +template +class snplib_session_manager_t +{ +public: + typedef std::shared_ptr session_ptr_t; + + session_ptr_t createSession(typename SessionT::context_t ctx) + { + std::lock_guard lock(_setMutex); + + auto sptr = std::make_shared(ctx); + _sessions.insert(sptr); + + return sptr; + } + + // + // delegates an implementation to the derived class + // + template SelfT> + auto startSession(this SelfT&& self, session_ptr_t ptr) + { + std::lock_guard lock(self._setMutex); + + return std::forward(self)(std::move(ptr)); + } + + bool stopSession(session_ptr_t ptr) + { + std::lock_guard lock(_setMutex); + + if (auto it = _sessions.find(ptr); it != _sessions.end()) { + if (auto sptr = it->lock()) { // it is safe in multi-threaded environment! + sptr->stopSession(); + } // session pointer expired here + } else { + return false; + } + + return true; + } + + size_t stopSessions() + { + std::lock_guard lock(_setMutex); + + size_t N = 0; + for (auto sess : _sessions) { + if (stopSession(sess)) { + ++N; + } + } + + return N; + } + +protected: + std::unique_ptr _setMutex{new std::recursive_mutex}; + std::set, std::owner_less>> _sessions{}; +}; + + + +template SessManagerT, + snplib_logger_c LoggerT> requires std::same_as -class snplib_generic_netserver_t +class snplib_generic_netserver_t : protected SessManagerT, public LoggerT { public: // typedef std::error_code error_t; - snplib_generic_netserver_t(std::unique_ptr listener) : _listener(std::move(listener)) {} + using LoggerT::logDebug; + using LoggerT::logError; + using LoggerT::logInfo; + using LoggerT::logTrace; + using LoggerT::logWarn; + + template + snplib_generic_netserver_t(std::unique_ptr listener) : _listener(std::move(listener)) + { + } + + virtual ~snplib_generic_netserver_t() = default; auto startServer() {