CMakeLists.txt: requires C++23 standard

CMakeLists.txt: add compiler version checks
adc_netserver.h: add AdcTrivialLogger class (use of 'deduced this'
feature of C++23 standard); AdcGenericNetServer class now has basic
based on std::basic_ostream logging capability
This commit is contained in:
Timur A. Fatkhullin 2024-11-11 18:36:54 +03:00
parent afa8d09ade
commit 6acc1f94ba
5 changed files with 143 additions and 20 deletions

View File

@ -4,10 +4,31 @@ project(ADC LANGUAGES CXX)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_BUILD_TYPE Release)
#
# check compiler version to ensure supporting of
# 'deducing this' C++23 feature
#
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0)
message(FATAL_ERROR "GCC version must be at least 14.0!")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0)
message(FATAL_ERROR "Clang version must be at least 18.0!")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32")
message(FATAL_ERROR "MSVC version must be at least 19.32")
endif()
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
endif()
set(ADC_COMMON_HEADERS
common/adc_traits.h
@ -29,7 +50,7 @@ set(ADC_NETWORK_HEADERS
# net/adc_netmsg.h
# net/adc_netmessage.h
net/adc_netproto.h
net/adc_netservice.h
# net/adc_netservice.h
net/adc_endpoint.h
net/adc_netserver.h
net/adc_net_concepts.h

View File

@ -246,9 +246,11 @@ public:
_netService.asyncReceive(
[self, this](netservice_t::async_callback_err_t ec, message_t msg) {
if (ec) {
std::string str("asyncReceive operation completed with error: ");
netservice_t::formatError(ec, str);
_serverPtr->errorMessage(str);
// std::string str("asyncReceive operation completed with error: ");
// netservice_t::formatError(ec, str);
// _serverPtr->errorMessage(str);
_serverPtr->logError("asyncReceive operation completed with error: {}",
netservice_t::formattableError(ec));
stop();
} else {
auto msg_sptr = std::make_shared<message_t>(std::move(msg));
@ -261,9 +263,11 @@ public:
*msg_sptr,
[self, msg_sptr, this](netservice_t::async_callback_err_t ec) {
if (ec) {
std::string str("asyncSend operation completed with error: ");
netservice_t::formatError(ec, str);
_serverPtr->errorMessage(str);
// std::string str("asyncSend operation completed with error: ");
// netservice_t::formatError(ec, str);
// _serverPtr->errorMessage(str);
_serverPtr->logError("asyncSend operation completed with error: {}",
netservice_t::formattableError(ec));
stop();
} else {
start();

View File

@ -160,8 +160,11 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
srv.close();
// static method
SRVT::formatError(std::declval<typename SRVT::async_callback_err_t>(), std::declval<std::string&>());
// // static method
// SRVT::formatError(std::declval<typename SRVT::async_callback_err_t>(), std::declval<std::string&>());
// convert given error to formattable (valid input to std::format) representation
{ SRVT::formattableError(std::declval<typename SRVT::async_callback_err_t>()) } -> traits::formattable;
};

View File

@ -8,6 +8,7 @@ ABSTRACT DEVICE COMPONENTS LIBRARY
#include <filesystem>
#include <functional>
#include <iostream>
#include <list>
#include <set>
#include <unordered_map>
@ -28,7 +29,87 @@ namespace adc
/* SOME USEFULL PRIVITIVES */
// Ageneric implementation pf POSIX OS daemon
// A simple std::ostream logger
class AdcTrivialLogger
{
public:
virtual ~AdcTrivialLogger() = default;
protected:
static constexpr std::string_view errorLevelMark{"error"};
static constexpr std::string_view warnLevelMark{"warning"};
static constexpr std::string_view infoLevelMark{"info"};
std::basic_ostream<char>& _stream;
AdcTrivialLogger(std::basic_ostream<char>& stream = std::cout) : _stream(stream) {}
template <traits::formattable... ArgTs>
std::string logMsgFormat(std::string_view level_mark, std::string_view fmt, ArgTs&&... args)
{
std::string s;
std::format_to(std::back_inserter(s), fmt, std::forward<ArgTs>(args)...);
std::stringstream st;
const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// format log-message in form:
// [YYYY-MM-DD HH:MM:SS][level] log-message
//
st << std::put_time(std::localtime(&now), "[%F %T]") << "[" << level_mark << "] " << s;
return st.str();
}
// define default logging function of ERROR, WARNING and INFO levels
template <traits::formattable... ArgTs>
void logError(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<std::remove_cvref_t<obj_t>, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(errorLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logError(fmt, std::forward<ArgTs>(args)...);
}
}
template <traits::formattable... ArgTs>
void logWarn(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<obj_t, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(warnLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logWarn(fmt, std::forward<ArgTs>(args)...);
}
}
template <traits::formattable... ArgTs>
void logInfo(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<obj_t, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(infoLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logInfo(fmt, std::forward<ArgTs>(args)...);
}
}
};
// A generic implementation of POSIX OS daemon
class AdcPosixGenericDaemon
{
public:
@ -224,12 +305,15 @@ protected:
/* very generic network server */
template <typename IdentT = std::string>
class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager
class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public AdcTrivialLogger
{
public:
typedef IdentT server_ident_t;
AdcGenericNetServer(const server_ident_t& id) : _serverIdent(id) {}
AdcGenericNetServer(const server_ident_t& id, std::basic_ostream<char>& log_stream = std::cout)
: AdcTrivialLogger(log_stream), _serverIdent(id)
{
}
AdcGenericNetServer(const AdcGenericNetServer&) = delete;
@ -353,16 +437,18 @@ protected:
_isListening<SessionT>[this][id] = true;
doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx));
} else {
std::string str{"Cannot start accepting connection: "};
SessionT::netservice_t::formatError(ec, str);
errorMessage(str);
// std::string str{"Cannot start accepting connection: "};
// SessionT::netservice_t::formatError(ec, str);
// errorMessage(str);
this->logError("Cannot start accepting connection: {}", SessionT::netservice_t::formattableError(ec));
_isListening<SessionT>[this][id] = false;
}
});
}
virtual void errorMessage(const std::string&) {};
// virtual void errorMessage(const std::string&) {};
};

View File

@ -758,10 +758,19 @@ public:
}
static void formatError(std::error_code err, std::string& result_str)
// static void formatError(std::error_code err, std::string& result_str)
// {
// std::format_to(std::back_inserter(result_str), "{} (Err category: {}) (Err msg: {})", err.value(),
// err.category().name(), err.message());
// }
static std::string formattableError(std::error_code ec)
{
std::format_to(std::back_inserter(result_str), "{} (Err category: {}) (Err msg: {})", err.value(),
err.category().name(), err.message());
std::string s;
std::format_to(std::back_inserter(s), "{} (Err category: {}) (Err msg: {})", ec.value(), ec.category().name(),
ec.message());
return s;
}
protected: