diff --git a/CMakeLists.txt b/CMakeLists.txt index 49c5cb8..084a7f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/net/adc_device_netserver.h b/net/adc_device_netserver.h index e228a5f..099ce7c 100644 --- a/net/adc_device_netserver.h +++ b/net/adc_device_netserver.h @@ -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(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(); diff --git a/net/adc_net_concepts.h b/net/adc_net_concepts.h index 93f02eb..bed4cb4 100644 --- a/net/adc_net_concepts.h +++ b/net/adc_net_concepts.h @@ -160,8 +160,11 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) { srv.close(); - // static method - SRVT::formatError(std::declval(), std::declval()); + // // static method + // SRVT::formatError(std::declval(), std::declval()); + + // convert given error to formattable (valid input to std::format) representation + { SRVT::formattableError(std::declval()) } -> traits::formattable; }; diff --git a/net/adc_netserver.h b/net/adc_netserver.h index 6e072d9..1a725f3 100644 --- a/net/adc_netserver.h +++ b/net/adc_netserver.h @@ -8,6 +8,7 @@ ABSTRACT DEVICE COMPONENTS LIBRARY #include #include +#include #include #include #include @@ -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& _stream; + + AdcTrivialLogger(std::basic_ostream& stream = std::cout) : _stream(stream) {} + + template + 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(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 + void logError(this auto&& self, std::string_view fmt, ArgTs&&... args) + { + using obj_t = decltype(self); + + if constexpr (std::same_as, AdcTrivialLogger>) { + std::forward(self)._stream + << std::forward(self).logMsgFormat(errorLevelMark, fmt, std::forward(args)...) << "\n" + << std::flush; + } else { + std::forward(self).logError(fmt, std::forward(args)...); + } + } + + template + void logWarn(this auto&& self, std::string_view fmt, ArgTs&&... args) + { + using obj_t = decltype(self); + + if constexpr (std::same_as) { + std::forward(self)._stream + << std::forward(self).logMsgFormat(warnLevelMark, fmt, std::forward(args)...) << "\n" + << std::flush; + } else { + std::forward(self).logWarn(fmt, std::forward(args)...); + } + } + + template + void logInfo(this auto&& self, std::string_view fmt, ArgTs&&... args) + { + using obj_t = decltype(self); + + if constexpr (std::same_as) { + std::forward(self)._stream + << std::forward(self).logMsgFormat(infoLevelMark, fmt, std::forward(args)...) << "\n" + << std::flush; + } else { + std::forward(self).logInfo(fmt, std::forward(args)...); + } + } +}; + + +// A generic implementation of POSIX OS daemon class AdcPosixGenericDaemon { public: @@ -224,12 +305,15 @@ protected: /* very generic network server */ template -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& log_stream = std::cout) + : AdcTrivialLogger(log_stream), _serverIdent(id) + { + } AdcGenericNetServer(const AdcGenericNetServer&) = delete; @@ -353,16 +437,18 @@ protected: _isListening[this][id] = true; doAccept(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[this][id] = false; } }); } - virtual void errorMessage(const std::string&) {}; + // virtual void errorMessage(const std::string&) {}; }; diff --git a/net/asio/adc_netservice_asio.h b/net/asio/adc_netservice_asio.h index 8575ed8..22f9e67 100644 --- a/net/asio/adc_netservice_asio.h +++ b/net/asio/adc_netservice_asio.h @@ -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: