From 92d1a3e5738383337d454b98837dc84ef229ec2e Mon Sep 17 00:00:00 2001 From: qicosmos Date: Sun, 21 Sep 2025 20:07:04 +0800 Subject: [PATCH] string_view no copy and no serialize --- include/rest_rpc/client.hpp | 19 +++- include/rest_rpc/codec.h | 42 ++++++- include/rest_rpc/cplusplus_14.h | 7 +- include/rest_rpc/meta_util.hpp | 8 +- include/rest_rpc/nonstd_any.hpp | 16 ++- include/rest_rpc/rest_rpc_protocol.hpp | 8 ++ include/rest_rpc/rpc_connection.hpp | 19 ++-- include/rest_rpc/rpc_router.hpp | 148 ++++++++++++++++--------- include/rest_rpc/traits.h | 44 +++++++- include/rest_rpc/util.hpp | 11 ++ tests/test_rest_rpc1.cpp | 56 ++++++++++ 11 files changed, 302 insertions(+), 76 deletions(-) create mode 100644 include/rest_rpc/util.hpp diff --git a/include/rest_rpc/client.hpp b/include/rest_rpc/client.hpp index e4744ce..2fadaee 100644 --- a/include/rest_rpc/client.hpp +++ b/include/rest_rpc/client.hpp @@ -6,8 +6,10 @@ #include "logger.hpp" #include "meta_util.hpp" #include "rest_rpc_protocol.hpp" +#include "string_resize.hpp" #include "traits.h" #include "use_asio.hpp" +#include "util.hpp" #include #include using namespace asio::experimental::awaitable_operators; @@ -18,6 +20,10 @@ template struct call_result { R value; }; +template <> struct call_result { + rpc_errc ec; +}; + class client { public: client() : socket_(get_global_executor()) {} @@ -121,7 +127,9 @@ private: std::vector buffers; buffers.reserve(2); buffers.push_back(asio::buffer(&header, sizeof(rest_rpc_header))); - buffers.push_back(asio::buffer(buf.data(), buf.size())); + if constexpr (sizeof...(Args) > 0) { + buffers.push_back(asio::buffer(buf.data(), buf.size())); + } using R = typename function_traits::return_type; call_result result{}; @@ -150,14 +158,19 @@ private: detail::resize(body_, resp_header.body_len); std::tie(ec, size) = co_await asio::async_read( - socket_, asio::buffer(body_), asio::as_tuple(asio::use_awaitable)); + socket_, asio::buffer(body_.data(), body_.size()), + asio::as_tuple(asio::use_awaitable)); if (ec) { REST_LOG_WARNING << "read body error: " << ec.message(); result.ec = rpc_errc::read_error; co_return result; } result.ec = (rpc_errc)body_[0]; - result.value = codec.unpack(body_.data() + 1, resp_header.body_len - 1); + if constexpr (!std::is_void_v) { + result.value = codec.unpack( + std::string_view(body_.data() + 1, resp_header.body_len - 1)); + } + co_return result; } diff --git a/include/rest_rpc/codec.h b/include/rest_rpc/codec.h index 07c1717..020fd76 100644 --- a/include/rest_rpc/codec.h +++ b/include/rest_rpc/codec.h @@ -1,19 +1,36 @@ #ifndef REST_RPC_CODEC_H_ #define REST_RPC_CODEC_H_ +#include "traits.h" +#include #include namespace rest_rpc { namespace rpc_service { +template auto pack_one(Arg &&arg) { + if constexpr (util::CharArrayRef || util::CharArray || + util::string) { + return std::string_view(std::forward(arg)); + } else { + return std::to_string(arg); + } +} + using buffer_type = msgpack::sbuffer; struct msgpack_codec { const static size_t init_size = 2 * 1024; - template static buffer_type pack_args(Args &&...args) { - buffer_type buffer(init_size); - msgpack::pack(buffer, std::forward_as_tuple(std::forward(args)...)); - return buffer; + template static auto pack_args(Args &&...args) { + if constexpr (sizeof...(Args) == 0) { + return std::string_view{}; + } else if constexpr (sizeof...(Args) == 1 && util::is_basic_v) { + return pack_one(std::forward(args)...); + } else { + buffer_type buffer(init_size); + msgpack::pack(buffer, std::forward_as_tuple(std::forward(args)...)); + return buffer; + } } template static std::string pack_to_string(Arg &arg) { @@ -46,6 +63,23 @@ struct msgpack_codec { } } + template T unpack(std::string_view data) { + if constexpr (std::is_fundamental_v) { + T t; + auto r = std::from_chars(data.data(), data.data() + data.size(), t); + if (r.ec != std::errc()) { + throw std::invalid_argument("unpack failed: Args not match!"); + } + return t; + } else if constexpr (std::is_same_v) { + return std::string(data); + } else if constexpr (std::is_same_v) { + return data; + } else { + return unpack(data.data(), data.size()); + } + } + private: msgpack::unpacked msg_; }; diff --git a/include/rest_rpc/cplusplus_14.h b/include/rest_rpc/cplusplus_14.h index cda912a..27ef02b 100644 --- a/include/rest_rpc/cplusplus_14.h +++ b/include/rest_rpc/cplusplus_14.h @@ -79,9 +79,10 @@ auto apply_helper(F &&f, Tuple &&tp, nonstd::index_sequence) } template -auto apply(F &&f, Tuple &&tp) -> decltype(apply_helper( - std::forward(f), std::forward(tp), - make_index_sequence>::value>{})) { +auto apply(F &&f, Tuple &&tp) + -> decltype(apply_helper( + std::forward(f), std::forward(tp), + make_index_sequence>::value>{})) { return apply_helper( std::forward(f), std::forward(tp), make_index_sequence>::value>{}); diff --git a/include/rest_rpc/meta_util.hpp b/include/rest_rpc/meta_util.hpp index a62f02a..4a45273 100644 --- a/include/rest_rpc/meta_util.hpp +++ b/include/rest_rpc/meta_util.hpp @@ -28,7 +28,7 @@ struct function_traits { public: enum { arity = sizeof...(Args) + 1 }; typedef Ret function_type(Arg, Args...); - typedef Ret return_type; + typedef std::remove_cvref_t return_type; using stl_function_type = std::function; typedef Ret (*pointer)(Arg, Args...); @@ -51,7 +51,7 @@ template struct function_traits { public: enum { arity = 0 }; typedef Ret function_type(); - typedef Ret return_type; + typedef std::remove_cvref_t return_type; using stl_function_type = std::function; typedef Ret (*pointer)(); @@ -123,7 +123,9 @@ using nth_type_of = nonstd::tuple_element_t>; template using last_type_of = nth_type_of; -template struct remove_first { using type = T; }; +template struct remove_first { + using type = T; +}; template struct remove_first> { diff --git a/include/rest_rpc/nonstd_any.hpp b/include/rest_rpc/nonstd_any.hpp index 7817ea9..69861f8 100644 --- a/include/rest_rpc/nonstd_any.hpp +++ b/include/rest_rpc/nonstd_any.hpp @@ -38,7 +38,7 @@ #define any_HAVE_TWEAK_HEADER 1 #else #define any_HAVE_TWEAK_HEADER 0 -//# pragma message("any.hpp: Note: Tweak header not supported.") +// # pragma message("any.hpp: Note: Tweak header not supported.") #endif // any selection and configuration: @@ -251,7 +251,7 @@ using std::swap; #endif // half-open range [lo..hi): -//#define any_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) +// #define any_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) // Presence of language and library features: @@ -387,7 +387,9 @@ using std::tr1::add_const; #else -template struct add_const { typedef const T type; }; +template struct add_const { + typedef const T type; +}; #endif // any_HAVE_ADD_CONST @@ -401,8 +403,12 @@ using std::tr1::remove_reference; #else -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; +template struct remove_reference { + typedef T type; +}; +template struct remove_reference { + typedef T type; +}; #endif // any_HAVE_REMOVE_REFERENCE diff --git a/include/rest_rpc/rest_rpc_protocol.hpp b/include/rest_rpc/rest_rpc_protocol.hpp index 1fe6927..367b25f 100644 --- a/include/rest_rpc/rest_rpc_protocol.hpp +++ b/include/rest_rpc/rest_rpc_protocol.hpp @@ -6,6 +6,14 @@ #include #endif +inline uint64_t htonll(uint64_t value) { + return ((uint64_t)htonl(value & 0xFFFFFFFF) << 32) | htonl(value >> 32); +} + +inline uint64_t ntohll(uint64_t value) { + return ((uint64_t)ntohl(value & 0xFFFFFFFF) << 32) | ntohl(value >> 32); +} + namespace rest_rpc { inline constexpr uint8_t REST_MAGIC_NUM = 39; struct rest_rpc_header { diff --git a/include/rest_rpc/rpc_connection.hpp b/include/rest_rpc/rpc_connection.hpp index 3e8ca05..0658ac0 100644 --- a/include/rest_rpc/rpc_connection.hpp +++ b/include/rest_rpc/rpc_connection.hpp @@ -32,24 +32,29 @@ public: } detail::resize(body_, header.body_len); - std::tie(ec, size) = co_await asio::async_read( - socket_, asio::buffer(body_), asio::as_tuple(asio::use_awaitable)); - if (ec) { - REST_LOG_WARNING << "read body error: " << ec.message(); - break; + + if (header.body_len > 0) { + std::tie(ec, size) = co_await asio::async_read( + socket_, asio::buffer(body_), asio::as_tuple(asio::use_awaitable)); + if (ec) { + REST_LOG_WARNING << "read body error: " << ec.message(); + break; + } } // route auto result = router_.route(header.function_id, body_); rest_rpc_header resp_header{}; resp_header.magic = 39; - resp_header.body_len = result.result.size() + 1; + resp_header.body_len = result.size() + 1; prepare_for_send(resp_header); std::vector buffers; buffers.reserve(3); buffers.push_back(asio::buffer(&resp_header, sizeof(rest_rpc_header))); buffers.push_back(asio::buffer(&result.ec, 1)); - buffers.push_back(asio::buffer(result.result)); + if (!result.empty()) + buffers.push_back(asio::buffer(result.data())); + std::tie(ec, size) = co_await asio::async_write( socket_, buffers, asio::as_tuple(asio::use_awaitable)); if (ec) { diff --git a/include/rest_rpc/rpc_router.hpp b/include/rest_rpc/rpc_router.hpp index a39cc26..d274eac 100644 --- a/include/rest_rpc/rpc_router.hpp +++ b/include/rest_rpc/rpc_router.hpp @@ -1,9 +1,9 @@ #pragma once #include "codec.h" #include "error_code.h" -#include "function_name.h" -#include "md5.hpp" + #include "meta_util.hpp" +#include "util.hpp" #include #include #include @@ -13,13 +13,12 @@ namespace rest_rpc { struct rpc_result { rpc_errc ec = rpc_errc::ok; std::string result; -}; + std::string_view view; + bool empty() { return result.empty() && view.empty(); } + size_t size() { return result.empty() ? view.size() : result.size(); } -template constexpr uint32_t get_key() { - constexpr auto name = get_func_name(); - constexpr uint32_t key = MD5::MD5Hash32(name.data(), name.length()); - return key; -} + std::string_view data() { return result.empty() ? view : result; } +}; class rpc_router { public: @@ -57,30 +56,28 @@ public: rpc_result route(uint32_t key, std::string_view data) { rpc_result route_result{}; - std::string result; try { rpc_service::msgpack_codec codec; auto it = map_invokers_.find(key); if (it == map_invokers_.end()) { - result = "unknown function: " + get_name_by_key(key); + route_result.result = "unknown function: " + get_name_by_key(key); route_result.ec = rpc_errc::no_such_function; } else { - it->second(data, route_result.ec, result); + it->second(data, route_result); route_result.ec = rpc_errc::ok; } } catch (const std::exception &ex) { rpc_service::msgpack_codec codec; - result = std::string("exception occur when call").append(ex.what()); + route_result.result = + std::string("exception occur when call").append(ex.what()); route_result.ec = rpc_errc::function_exception; } catch (...) { rpc_service::msgpack_codec codec; - result = std::string("unknown exception occur when call ") - .append(get_name_by_key(key)); + route_result.result = std::string("unknown exception occur when call ") + .append(get_name_by_key(key)); route_result.ec = rpc_errc::function_unknown_exception; } - route_result.result = std::move(result); - return route_result; } @@ -103,65 +100,116 @@ private: template void register_nonmember_func(uint32_t key, Function f) { this->map_invokers_[key] = [f = std::move(f)](std::string_view str, - rpc_errc &ec, - std::string &result) mutable { + rpc_result &ret) mutable { using args_tuple = typename function_traits::tuple_type; using R = typename function_traits::return_type; - rpc_service::msgpack_codec codec; + try { - auto tp = codec.unpack(str.data(), str.size()); - if constexpr (std::is_void_v) { - std::apply(f, tp); + if constexpr (std::tuple_size_v == 0) { + if constexpr (std::is_void_v) { + f(); + } else { + auto r = f(); + ret.result = rpc_service::msgpack_codec::pack_to_string(r); + } } else { - auto r = std::apply(f, tp); - result = rpc_service::msgpack_codec::pack_to_string(r); + rpc_service::msgpack_codec codec; + using first_t = std::tuple_element_t<0, args_tuple>; + if constexpr (std::tuple_size_v == 1 && + util::is_basic_v) { + if constexpr (std::is_void_v) { + f(codec.unpack(str)); + } else { + if constexpr (std::is_same_v) { + ret.view = rpc_service::msgpack_codec::pack_args( + f(codec.unpack(str))); + } else { + ret.result = rpc_service::msgpack_codec::pack_args( + f(codec.unpack(str))); + } + } + } else { + auto tp = codec.unpack(str); + if constexpr (std::is_void_v) { + std::apply(f, tp); + } else { + auto r = std::apply(f, tp); + ret.result = rpc_service::msgpack_codec::pack_to_string(r); + } + } } } catch (std::invalid_argument &e) { - ec = rpc_errc::invalid_argument; - result = e.what(); + ret.ec = rpc_errc::invalid_argument; + ret.result = e.what(); } catch (const std::exception &e) { - ec = rpc_errc::function_exception; - result = e.what(); + ret.ec = rpc_errc::function_exception; + ret.result = e.what(); } }; } template void register_member_func(uint32_t key, const Function &f, Self *self) { - this->map_invokers_[key] = [f, self](std::string_view str, rpc_errc &ec, - std::string &result) { + this->map_invokers_[key] = [f, self](std::string_view str, + rpc_result &ret) { using args_tuple = typename function_traits::tuple_type; using R = typename function_traits::return_type; rpc_service::msgpack_codec codec; try { - auto tp = codec.unpack(str.data(), str.size()); - - if constexpr (std::is_void_v) { - std::apply( - [self, &f](auto &&...args) { - return (*self.*f)(std::forward(args)...); - }, - tp); + if constexpr (std::tuple_size_v == 0) { + if constexpr (std::is_void_v) { + (*self.*f)(); + } else { + auto r = (*self.*f)(); + ret.result = rpc_service::msgpack_codec::pack_to_string(r); + } } else { - auto r = std::apply( - [self, &f](auto &&...args) { - return (*self.*f)(std::forward(args)...); - }, - tp); - result = rpc_service::msgpack_codec::pack_to_string(r); + using first_t = std::tuple_element_t<0, args_tuple>; + if constexpr (std::tuple_size_v == 1 && + util::is_basic_v) { + if constexpr (std::is_void_v) { + (*self.*f)(codec.unpack(str)); + } else { + if constexpr (std::is_same_v) { + ret.view = rpc_service::msgpack_codec::pack_args( + (*self.*f)(codec.unpack(str))); + } else { + ret.result = rpc_service::msgpack_codec::pack_args( + (*self.*f)(codec.unpack(str))); + } + } + } else { + auto tp = codec.unpack(str); + + if constexpr (std::is_void_v) { + std::apply( + [self, &f](auto &&...args) { + return (*self.*f)(std::forward(args)...); + }, + tp); + } else { + auto r = std::apply( + [self, &f](auto &&...args) { + return (*self.*f)(std::forward(args)...); + }, + tp); + ret.result = rpc_service::msgpack_codec::pack_to_string(r); + } + } } + } catch (std::invalid_argument &e) { - ec = rpc_errc::invalid_argument; - result = e.what(); + ret.ec = rpc_errc::invalid_argument; + ret.result = e.what(); } catch (const std::exception &e) { - ec = rpc_errc::function_exception; - result = e.what(); + ret.ec = rpc_errc::function_exception; + ret.result = e.what(); } }; } - std::unordered_map> + std::unordered_map> map_invokers_; std::unordered_map key2func_name_; }; diff --git a/include/rest_rpc/traits.h b/include/rest_rpc/traits.h index 44a3d5e..80fe55a 100644 --- a/include/rest_rpc/traits.h +++ b/include/rest_rpc/traits.h @@ -145,7 +145,9 @@ struct is_invocable template inline constexpr bool is_invocable_v = is_invocable::value; -template struct remove_first { using type = T; }; +template struct remove_first { + using type = T; +}; template struct remove_first> { @@ -171,4 +173,44 @@ struct is_specialization, Ref> : std::true_type {}; template class Ref> inline constexpr bool is_specialization_v = is_specialization::value; + +template +concept container = requires(Type container) { + typename std::remove_cvref_t::value_type; + container.size(); + container.begin(); + container.end(); +}; + +template +constexpr bool is_char_t = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v +#ifdef __cpp_lib_char8_t + || std::is_same_v +#endif + ; + +template +concept string = container && requires(Type container) { + requires is_char_t::value_type>; + container.length(); + container.data(); +}; + +template +concept CharArrayRef = requires { + requires std::is_array_v> && + std::same_as>, char>; +}; + +template +concept CharArray = + std::is_array_v> && + std::same_as>, char>; + +template +inline constexpr bool is_basic_v = + std::is_fundamental_v || string || CharArray || CharArrayRef; } // namespace rest_rpc::util diff --git a/include/rest_rpc/util.hpp b/include/rest_rpc/util.hpp new file mode 100644 index 0000000..309dc45 --- /dev/null +++ b/include/rest_rpc/util.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "function_name.h" +#include "md5.hpp" + +namespace rest_rpc { +template constexpr uint32_t get_key() { + constexpr auto name = get_func_name(); + constexpr uint32_t key = MD5::MD5Hash32(name.data(), name.length()); + return key; +} +} // namespace rest_rpc \ No newline at end of file diff --git a/tests/test_rest_rpc1.cpp b/tests/test_rest_rpc1.cpp index 3c30dea..8fbe2d4 100644 --- a/tests/test_rest_rpc1.cpp +++ b/tests/test_rest_rpc1.cpp @@ -12,21 +12,48 @@ struct dummy { int add(int a, int b) { return a + b; } void foo(std::string str) { std::cout << str << "\n"; } + + std::string echo(std::string val) { return val; } + + int round1(int i) { return i; } }; int add(int a, int b) { return a + b; } void foo(std::string str) { std::cout << str << "\n"; } +int round1(int i) { return i; } + +std::string_view echo_sv(std::string_view str) { return str; } + +std::string echo(std::string str) { return str; } + +void no_arg() { std::cout << "no args\n"; } + TEST_CASE("test router") { rpc_router router; router.register_handler(); router.register_handler(); + router.register_handler(); + router.register_handler(); dummy d{}; router.register_handler<&dummy::add>(&d); router.register_handler<&dummy::foo>(&d); + router.register_handler<&dummy::round1>(&d); + router.register_handler<&dummy::echo>(&d); rpc_service::msgpack_codec codec; + { + auto s = codec.pack_args(1); + auto s1 = codec.pack_args("test"); + auto r = router.route(get_key(), s); + auto r1 = router.route(get_key(), s1); + + auto r2 = router.route(get_key<&dummy::round1>(), s); + auto r3 = router.route(get_key<&dummy::echo>(), s1); + std::cout << "\n"; + } + auto args = codec.pack_args(1, 2); std::string_view str(args.data(), args.size()); @@ -54,9 +81,23 @@ TEST_CASE("test server start") { rest_rpc_server server("127.0.0.1:9005"); server.register_handler(); server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); auto ec = server.async_start(); CHECK(!ec); client cl; + + static_assert(util::CharArrayRef); + static_assert(util::CharArray); + rpc_service::msgpack_codec::pack_args(); + rpc_service::msgpack_codec::pack_args(1, 2); + auto s1 = rpc_service::msgpack_codec::pack_args("test"); + auto s2 = rpc_service::msgpack_codec::pack_args(std::string_view("test2")); + auto s3 = rpc_service::msgpack_codec::pack_args(std::string("test2")); + auto s5 = rpc_service::msgpack_codec::pack_args(123); + // auto future = asio::co_spawn(cl.get_executor(), // cl.connect("127.0.0.1:9005"), asio::use_future); auto conn_ec = // future.get(); @@ -72,8 +113,23 @@ TEST_CASE("test server start") { // auto future1 = asio::co_spawn(cl.get_executor(), cl.call(1, 2), // asio::use_future); auto result = future1.get(); + { + // auto result = sync_wait(cl.get_executor(), + // cl.call_for(std::chrono::minutes(2))); + // CHECK(result.ec==rpc_errc::ok); + auto result = + sync_wait(cl.get_executor(), + cl.call_for(std::chrono::minutes(2), "test")); + CHECK(result.ec == rpc_errc::ok); + auto result1 = sync_wait( + cl.get_executor(), cl.call_for(std::chrono::minutes(2), "test")); + CHECK(result1.ec == rpc_errc::ok); + auto result2 = sync_wait(cl.get_executor(), cl.call(1)); + CHECK(result2.ec == rpc_errc::ok); + } auto result = sync_wait(cl.get_executor(), cl.call(1, 2)); + CHECK(result.ec == rpc_errc::ok); ec = server.async_start(); CHECK(!ec);