...
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#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 <typename T>
|
||||
concept snplib_net_session_c = std::derived_from<T, std::enable_shared_from_this<T>> && requires(T t) {
|
||||
// static constexpr member
|
||||
std::formattable<decltype(T::id), char>;
|
||||
[]() { [[maybe_unused]] static constexpr auto val = T::id; }();
|
||||
concept snplib_net_session_c =
|
||||
std::derived_from<T, std::enable_shared_from_this<T>> &&
|
||||
requires(T t, const T t_const) {
|
||||
typename T::ident_t;
|
||||
typename T::context_t;
|
||||
|
||||
snplib_net_connection_c<typename T::connection_t>;
|
||||
snplib_net_connection_c<typename T::connection_t>;
|
||||
|
||||
// { t.startSession(std::declval<typename T::connection_t>()) } -> snplib_async_retval_c;
|
||||
t.startSession(std::declval<typename T::connection_t>());
|
||||
{ t_const.ident() } -> std::same_as<typename T::ident_t>;
|
||||
|
||||
t.stopSession();
|
||||
};
|
||||
// { t.startSession(std::declval<typename T::connection_t>()) } -> snplib_async_retval_c;
|
||||
t.startSession(std::declval<typename T::connection_t>());
|
||||
|
||||
t.stopSession();
|
||||
} && (!std::is_void_v<typename T::context_t> || std::constructible_from<T, typename T::ident_t>) &&
|
||||
(std::is_void_v<typename T::context_t> ||
|
||||
(std::constructible_from<T, typename T::ident_t, typename T::context_t> ||
|
||||
std::constructible_from<T, typename T::ident_t, typename T::context_t&>)); // can be constructible from value or
|
||||
// lvalue-reference or const
|
||||
// lvalue-reference of context_t-type
|
||||
|
||||
|
||||
template <typename T>
|
||||
@@ -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 <typename T>
|
||||
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 <typename T>
|
||||
concept snplib_net_initiator_c =
|
||||
requires(T t) {
|
||||
// common part
|
||||
requires snplib_net_initiator_role_c<decltype(T::ROLE)>;
|
||||
[]() { [[maybe_unused]] static constexpr auto val = T::ROLE; }();
|
||||
|
||||
snplib_net_connection_c<typename T::connection_t>;
|
||||
} && ((T::ROLE != decltype(T::ROLE)::ROLE_SERVER) || requires(T t) { // use of short-circuiting logic!!!
|
||||
// server-role
|
||||
{ t.accept() } -> snplib_async_retval_c<typename T::connection_t>;
|
||||
|
||||
{ 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<typename T::connection_t>;
|
||||
t.close();
|
||||
});
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept snplib_netserver_c = requires(T t) {
|
||||
// static constexpr member
|
||||
@@ -104,17 +153,118 @@ concept snplib_netserver_c = requires(T t) {
|
||||
};
|
||||
|
||||
|
||||
template <typename T, typename SessionT>
|
||||
concept snplib_session_manager_c =
|
||||
requires(T t, SessionT::context_t cnx) {
|
||||
requires snplib_net_session_c<SessionT>;
|
||||
|
||||
typename T::session_ptr_t; // e.g. std::shared_ptr<SessionT>
|
||||
|
||||
// must be a non-blocking method!
|
||||
t.startSession(std::declval<typename T::session_ptr_t>());
|
||||
|
||||
t.stopSession(std::declval<typename T::session_ptr_t>());
|
||||
|
||||
// must return number of stopped sessions
|
||||
{ t.stopSessions() } -> std::convertible_to<size_t>;
|
||||
|
||||
// true - if session exists
|
||||
{ t.isActive(std::declval<typename T::session_ptr_t>()) } -> std::convertible_to<bool>;
|
||||
} && (!std::is_void_v<typename SessionT::context_t> || requires(T t) {
|
||||
{ t.createSession() } -> std::same_as<typename T::session_ptr_t>;
|
||||
}) && (std::is_void_v<typename SessionT::context_t> || requires(T t, SessionT::context_t cnx) {
|
||||
{ t.createSession(cnx) } -> std::same_as<typename T::session_ptr_t>;
|
||||
});
|
||||
;
|
||||
|
||||
|
||||
/* SINGLE-PROTO*/
|
||||
|
||||
template <snplib_net_listener_c ListenerT, snplib_net_session_c SessionT>
|
||||
template <snplib_net_session_c SessionT>
|
||||
class snplib_session_manager_t
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<SessionT> session_ptr_t;
|
||||
|
||||
session_ptr_t createSession(typename SessionT::context_t ctx)
|
||||
{
|
||||
std::lock_guard lock(_setMutex);
|
||||
|
||||
auto sptr = std::make_shared<SessionT>(ctx);
|
||||
_sessions.insert(sptr);
|
||||
|
||||
return sptr;
|
||||
}
|
||||
|
||||
//
|
||||
// delegates an implementation to the derived class
|
||||
//
|
||||
template <std::derived_from<snplib_session_manager_t> SelfT>
|
||||
auto startSession(this SelfT&& self, session_ptr_t ptr)
|
||||
{
|
||||
std::lock_guard lock(self._setMutex);
|
||||
|
||||
return std::forward<SelfT>(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<std::recursive_mutex> _setMutex{new std::recursive_mutex};
|
||||
std::set<std::weak_ptr<SessionT>, std::owner_less<std::weak_ptr<SessionT>>> _sessions{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <snplib_net_listener_c ListenerT,
|
||||
snplib_net_session_c SessionT,
|
||||
snplib_session_manager_c<SessionT> SessManagerT,
|
||||
snplib_logger_c LoggerT>
|
||||
requires std::same_as<typename ListenerT::connection_t, typename SessionT::connection_t>
|
||||
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<ListenerT> listener) : _listener(std::move(listener)) {}
|
||||
using LoggerT::logDebug;
|
||||
using LoggerT::logError;
|
||||
using LoggerT::logInfo;
|
||||
using LoggerT::logTrace;
|
||||
using LoggerT::logWarn;
|
||||
|
||||
template <typename... SessManagerCtorArgTs, typename... LoggerCtorArgTs>
|
||||
snplib_generic_netserver_t(std::unique_ptr<ListenerT> listener) : _listener(std::move(listener))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~snplib_generic_netserver_t() = default;
|
||||
|
||||
auto startServer()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user