From af04681b3c91a6c46a8525a527106e1c68609827 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Wed, 30 Sep 2026 18:16:13 +0300 Subject: [PATCH] ... --- CMakeLists.txt | 54 +++++++++++++++++- examples/net_exam.cpp | 20 +++---- include/snipplib/concepts/snplib_concepts.h | 45 +++++++++++++-- include/snipplib/network/snplib_netproto.h | 56 ++++++++++++++----- include/snipplib/network/snplib_network.h | 40 +++++++------ .../snipplib/network/snplib_network_asio.h | 55 ++++++++++++++++++ 6 files changed, 223 insertions(+), 47 deletions(-) create mode 100644 include/snipplib/network/snplib_network_asio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 33fad03..c2e22d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,12 @@ option(SNPLIB_BUILD_EXAMPLES "Build examples" ON) option(SNPLIB_USE_SPDLOG "Use of spdlog library" ON) +option( + SNPLIB_USE_ASIO + "add network related implementation using ASIO library" + ON +) + if(SNPLIB_USE_SPDLOG) include(FetchContent) # find_package(spdlog CONFIG) @@ -45,8 +51,47 @@ set(LIB_HEADERS include/snipplib/containers/snplib_hvec.h include/snipplib/serialization/snplib_serialization.h include/snipplib/network/snplib_endpoint.h + include/snipplib/network/snplib_network.h ) +if(SNPLIB_USE_ASIO) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(ASIOLIB IMPORTED_TARGET GLOBAL asio) + + if(NOT ASIOLIB_FOUND) + message(STATUS "\tfetch asio-lib ...") + + FetchContent_Declare( + asiolib_project + PREFIX + ${CMAKE_BINARY_DIR}/asio + GIT_REPOSITORY "https://github.com/chriskohlhoff/asio.git" + GIT_TAG "asio-1-38-0" + GIT_SHALLOW 1 + ) + + FetchContent_MakeAvailable(asiolib_project) + + execute_process( + WORKING_DIRECTORY ${asiolib_project_SOURCE_DIR}/asio + COMMAND ./autogen.sh + ) + + execute_process( + WORKING_DIRECTORY ${asiolib_project_SOURCE_DIR}/asio + COMMAND ./configure --prefix=${asiolib_project_SOURCE_DIR}/asio + ) + + set(ENV{PKG_CONFIG_PATH} "${asiolib_project_SOURCE_DIR}/asio") + pkg_check_modules(ASIOLIB IMPORTED_TARGET GLOBAL asio) + + message(STATUS "ASIO INC PATHS: ${ASIOLIB_INCLUDE_DIRS}") + endif() + + list(APPEND LIB_HEADERS include/snipplib/network/snplib_network_asio.h) +endif(SNPLIB_USE_ASIO) + add_library(${PROJECT_NAME} INTERFACE ${LIB_HEADERS}) target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_23) target_include_directories( @@ -55,6 +100,9 @@ target_include_directories( "$" "$" ) + +# add dependencies + if(SNPLIB_USE_SPDLOG) target_compile_definitions( ${PROJECT_NAME} @@ -64,7 +112,11 @@ if(SNPLIB_USE_SPDLOG) SNPLIB_SPDLOGGER=1 ) target_link_libraries(${PROJECT_NAME} INTERFACE spdlog::spdlog_header_only) -endif() +endif(SNPLIB_USE_SPDLOG) + +if(SNPLIB_USE_ASIO) + target_link_libraries(${PROJECT_NAME} INTERFACE PkgConfig::ASIOLIB) +endif(SNPLIB_USE_ASIO) if(SNPLIB_BUILD_EXAMPLES) set(EXAM_STRING str_exam) diff --git a/examples/net_exam.cpp b/examples/net_exam.cpp index 0c4ae1e..fcc0533 100644 --- a/examples/net_exam.cpp +++ b/examples/net_exam.cpp @@ -17,7 +17,7 @@ int main() std::println("BYTES VEC: {}", bytes_vec); - auto msg = proto.fromBytes(bytes_vec); + auto msg = proto.fromTransport(bytes_vec); if (msg) { std::println("MSG (from std::vector): {}", msg.value()); @@ -25,7 +25,7 @@ int main() std::println("MSG ERR: {}", msg.error().message()); } - auto msg_l = proto.fromBytes(bytes_lst); + auto msg_l = proto.fromTransport(bytes_lst); if (msg_l) { std::println("MSG (from std::list): {}", msg_l.value()); @@ -33,7 +33,7 @@ int main() std::println("MSG ERR: {}", msg_l.error().message()); } - auto vmsg = proto.fromBytes>(bytes_lst); + auto vmsg = proto.fromTransport>(bytes_lst); if (vmsg) { std::println("VMSG (from std::list): {}", vmsg.value()); } else { @@ -49,13 +49,13 @@ int main() std::println("MSG: {}", bytes_vec); - auto bs = proto.toBytes(bytes_vec); + auto bs = proto.toTransport(bytes_vec); std::println("BYTES (from std::vector): {}", bs); - auto bs1 = proto.toBytes(bytes_lst); + auto bs1 = proto.toTransport(bytes_lst); std::println("BYTES (from std::list): {}", bs1); - bytes_vec = proto.toBytes(bytes_lst); + bytes_vec = proto.toTransport(bytes_lst); std::println("BYTES: {}", bytes_vec); @@ -71,24 +71,24 @@ int main() std::println("BLOB: {}", bytes); - bs = bb_proto.toBytes(bytes_vec); + bs = bb_proto.toTransport(bytes_vec); std::println("BYTES (from std::vector): {}", bs); - auto bs2 = bb_proto.toBytes(bytes_lst); + auto bs2 = bb_proto.toTransport(bytes_lst); std::println("BYTES (from std::list): {}", bs2); auto blob = std::vector(std::from_range, bs | std::views::join); std::println("\nBYTES: {}", blob); - vmsg = bb_proto.fromBytes>(blob); + vmsg = bb_proto.fromTransport>(blob); if (vmsg) { std::println("BLOB (from std::vector): {}", vmsg.value()); } else { std::println("VMSG ERR: {}", vmsg.error().message()); } - // std::array arr = bb_proto.toBytes(bytes_lst); + // std::array arr = bb_proto.toTransport(bytes_lst); return 0; } \ No newline at end of file diff --git a/include/snipplib/concepts/snplib_concepts.h b/include/snipplib/concepts/snplib_concepts.h index 89ab5e0..aa07435 100644 --- a/include/snipplib/concepts/snplib_concepts.h +++ b/include/snipplib/concepts/snplib_concepts.h @@ -6,6 +6,9 @@ #include #include + +#include + namespace snplib { @@ -194,12 +197,22 @@ concept ret_expected_type_c = requires { // result of asynchronous callable +template > +struct has_co_await_operator : std::false_type {}; + +template +struct has_co_await_operator().operator co_await())>> : std::true_type {}; + +template +constexpr bool has_co_await_operator_v = has_co_await_operator::value; + + template concept result_awaiter_c = requires(T t, std::coroutine_handle<> hndl) { { t.await_ready() } -> std::convertible_to; // await_suspend can return void, bool, or a coroutine_handle - t.await_suspend(hndl); + // t.await_suspend(hndl); // await_resume must return the specific type UT { t.await_resume() } -> std::same_as; @@ -207,13 +220,37 @@ concept result_awaiter_c = requires(T t, std::coroutine_handle<> hndl) { template -concept result_awaitable_c = result_awaiter_c || requires(T t) { - { t.operator co_await() } -> result_awaiter_c; +concept result_awaitable_c = requires(T t) { + // { t.operator co_await() } -> result_awaiter_c; + t.operator co_await(); }; +// template +// concept result_awaitable_c = result_awaiter_c || requires(T t) { +// { t.operator co_await() } -> result_awaiter_c; +// }; +template +concept awaitable_type_c = + (!has_co_await_operator_v || + std::same_as().operator co_await())>().await_resume()), RT>) && + (has_co_await_operator_v || std::same_as().await_resume()), RT>); + + +static_assert(awaitable_type_c>, "!!!"); template -concept async_retval_c = result_awaitable_c && ret_expected_c; +concept async_retval_c = result_awaitable_c().operator co_await())> || requires { + [] -> auto { + T t; + co_await t; + }(); +}; +// template +// concept async_retval_c = result_awaiter_c().operator co_await()), UT>; + +// template +// concept async_retval_c = result_awaitable_c && ret_expected_c; +}; // namespace snplib diff --git a/include/snipplib/network/snplib_netproto.h b/include/snipplib/network/snplib_netproto.h index d215669..c86a5ba 100644 --- a/include/snipplib/network/snplib_netproto.h +++ b/include/snipplib/network/snplib_netproto.h @@ -33,7 +33,7 @@ struct netproto_stopseq_t { // returns 2-element range of the input messsage and stop-sequence template requires char_range_c> // range of char ranges - OR toBytes(IR&& message) + OR toTransport(IR&& message) { OR res; @@ -45,7 +45,7 @@ struct netproto_stopseq_t { // returns a copy of the input message + tralling stop-sequence template - OR toBytes(IR&& message) + OR toTransport(IR&& message) { OR res; @@ -56,7 +56,7 @@ struct netproto_stopseq_t { } template - auto toBytes(IR&& message) + auto toTransport(IR&& message) { if constexpr (std::ranges::contiguous_range) { return std::array{std::span{message}, std::span{NETPROTO_STOP_SEQ}}; @@ -67,7 +67,7 @@ struct netproto_stopseq_t { } template - std::expected fromBytes(IR&& bytes) + std::expected fromTransport(IR&& bytes) { auto found = std::ranges::search(std::forward(bytes), NETPROTO_STOP_SEQ); if (found.empty()) { @@ -122,22 +122,22 @@ struct netproto_stopseq_t { // deduces a view-type from the input range-type template - auto fromBytes(IR&& bytes) + auto fromTransport(IR&& bytes) requires input_char_range_c> { using or_t = std::conditional_t, std::span, std::ranges::subrange, std::ranges::sentinel_t>>; - return fromBytes(std::forward(bytes)); + return fromTransport(std::forward(bytes)); } // to satisfy 'net_proto_c' concept template requires(std::same_as || std::same_as) - std::expected, std::error_code> fromBytes(std::span bytes) + std::expected, std::error_code> fromTransport(std::span bytes) { - return fromBytes>(bytes); + return fromTransport>(bytes); } }; @@ -148,11 +148,11 @@ static_assert(net_proto_c>, "!!!"); /* BINARY BLOB PROTOCOL */ /* - * a sequence of bytes in format: - * - * - blob size field: a symbolic representation of a HEXIMAL UNSIGNED INTEGER (e.g. "000000FA", MUST BE IN - UPPER-CASE!!!) + a sequence of bytes in format: + + + blob size field: a symbolic representation of a HEXIMAL UNSIGNED INTEGER + (e.g. "000000FA", MUST BE IN UPPER-CASE!!!) */ template @@ -164,7 +164,7 @@ struct netproto_binary_blob_t { static constexpr std::array BLOB_SIZE_FIELD_FORMAT{'{', ':', '0', '0' + BLOB_SIZE_FIELD, 'X', '}'}; template - auto toBytes(IR&& message) + auto toTransport(IR&& message) { static thread_local std::string Nbytes{}; @@ -182,7 +182,7 @@ struct netproto_binary_blob_t { template - std::expected fromBytes(IR&& bytes) + std::expected fromTransport(IR&& bytes) { using inp_t = std::remove_cvref_t; @@ -255,6 +255,32 @@ struct netproto_binary_blob_t { static_assert(false, "Unsupported output byte sequence type!"); } } + + // deduces a view-type from the input range-type + template + auto fromTransport(IR&& bytes) + requires input_char_range_c> + { + using or_t = + std::conditional_t, std::span, + std::ranges::subrange, std::ranges::sentinel_t>>; + + return fromTransport(std::forward(bytes)); + } + + // to satisfy 'net_proto_c' concept + template + requires(std::same_as || std::same_as) + std::expected, std::error_code> fromTransport(std::span bytes) + { + return fromTransport>(bytes); + } }; + + +static_assert(net_proto_c>, "!!!"); + + + } // namespace snplib \ No newline at end of file diff --git a/include/snipplib/network/snplib_network.h b/include/snipplib/network/snplib_network.h index 72bd567..d7c87ff 100644 --- a/include/snipplib/network/snplib_network.h +++ b/include/snipplib/network/snplib_network.h @@ -12,7 +12,7 @@ namespace snplib template concept net_endpoint_c = requires(T t, const T t_const) { // static constexpr member - std::formattable; + requires std::formattable; []() { [[maybe_unused]] static constexpr auto val = T::id; }(); { t.endpoint() } -> char_range_c; @@ -27,21 +27,19 @@ concept net_endpoint_c = requires(T t, const T t_const) { template > concept net_proto_c = requires(T t) { // from session-level representation to transport-level one - { t.toBytes(std::declval()) } -> char_range_or_range_of_char_ranges_c; + { t.toTransport(std::declval()) } -> char_range_or_range_of_char_ranges_c; // from transport-level representation to session-level one requires( requires { - // { t.fromBytes(std::declval>()) } -> char_range_c; - { t.fromBytes(std::declval>()) } -> ret_expected_c; + { t.fromTransport(std::declval>()) } -> ret_expected_c; requires char_range_c().fromBytes(std::declval>()).value())>>; + decltype(std::declval().fromTransport(std::declval>()).value())>>; } || requires { - // { t.fromBytes(std::declval>()) } -> char_range_c; - { t.fromBytes(std::declval>()) } -> ret_expected_c; - requires char_range_c< - std::remove_cvref_t().fromBytes(std::declval>()).value())>>; + { t.fromTransport(std::declval>()) } -> ret_expected_c; + requires char_range_c().fromTransport(std::declval>()).value())>>; }); }; @@ -66,13 +64,21 @@ concept net_message_c = requires(const T t_const) { template concept net_connection_c = requires(T t) { // static constexpr member - std::formattable; + requires std::formattable; []() { [[maybe_unused]] static constexpr auto val = T::id; }(); typename T::message_t; // minimal allowed unit of read/write operations + typename T::error_t; - { t.send(std::declval()) } -> async_retval_c; - { t.recv() } -> async_retval_c; + // { t.send(std::declval()) } -> async_retval_c; + { t.send(std::declval()) } -> awaitable_type_c; + // { t.recv() } -> async_retval_c; + { t.recv() } -> awaitable_type_c; + + // [] { + // typename T::message_t msg; + // co_await t.send(msg); + // }(); t.close(); }; @@ -85,7 +91,7 @@ concept net_session_c = typename T::ident_t; typename T::context_t; - net_connection_c; + requires net_connection_c; { t_const.ident() } -> std::same_as; @@ -104,10 +110,10 @@ concept net_session_c = template concept net_listener_c = requires(T t, const T t_const) { // static constexpr member - std::formattable; + requires std::formattable; []() { [[maybe_unused]] static constexpr auto val = T::id; }(); - net_connection_c; + requires net_connection_c; { t.accept() } -> async_retval_c; @@ -144,7 +150,7 @@ concept net_initiator_c = // to be sure 'ROLE' is compile-time constant []() { [[maybe_unused]] static constexpr auto val = T::ROLE; }(); - net_connection_c; + requires net_connection_c; { t_const.endpoint() } -> net_endpoint_c; } && ((T::ROLE != decltype(T::ROLE)::ROLE_SERVER) || requires(T t) { // use of short-circuiting logic!!! @@ -165,7 +171,7 @@ concept net_initiator_c = template concept netserver_c = requires(T t) { // static constexpr member - std::formattable; + requires std::formattable; []() { [[maybe_unused]] static constexpr auto val = T::id; }(); diff --git a/include/snipplib/network/snplib_network_asio.h b/include/snipplib/network/snplib_network_asio.h new file mode 100644 index 0000000..6a5c466 --- /dev/null +++ b/include/snipplib/network/snplib_network_asio.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "snplib_netmsg.h" +#include "snplib_netproto.h" +#include "snplib_network.h" + +namespace snplib::asio +{ +template +class asio_net_connection_t +{ +public: + typedef NETMSG_T message_t; + typedef NETPROTO_T netproto_t; + + typedef std::error_code error_t; + typedef std::expected result_t; + + static constexpr std::string_view id{"ASIO-NETCONN"}; + + // ::asio::awaitable send(asio_net_connection_t::message_t msg) + // { + // char data[1024]; + // size_t n = 1024; + + // auto executor = co_await ::asio::this_coro::executor; + // ::asio::ip::tcp::acceptor acceptor(executor, {::asio::ip::tcp::v4(), 55555}); + + // ::asio::ip::tcp::socket socket = co_await acceptor.async_accept(::asio::use_awaitable); + + // co_await ::asio::async_write(socket, ::asio::buffer(data, n), ::asio::use_awaitable); + // } + + // ::asio::awaitable recv() {} + + // ::asio::awaitable close() {} + + // ::asio::experimental::coro send(asio_net_connection_t::message_t msg) {} + ::asio::awaitable send(asio_net_connection_t::message_t msg) {} + + ::asio::awaitable recv() {} + + ::asio::awaitable close() {} +}; + + +static_assert(net_connection_c, netproto_stopseq_t<>>>, "!!!"); + +} // namespace snplib::asio \ No newline at end of file