string_view no copy and no serialize

This commit is contained in:
qicosmos
2025-09-21 20:07:04 +08:00
parent bf1c8747a4
commit 92d1a3e573
11 changed files with 302 additions and 76 deletions
+15 -2
View File
@@ -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 <asio/experimental/awaitable_operators.hpp>
#include <asio/steady_timer.hpp>
using namespace asio::experimental::awaitable_operators;
@@ -18,6 +20,10 @@ template <typename R> struct call_result {
R value;
};
template <> struct call_result<void> {
rpc_errc ec;
};
class client {
public:
client() : socket_(get_global_executor()) {}
@@ -121,7 +127,9 @@ private:
std::vector<asio::const_buffer> buffers;
buffers.reserve(2);
buffers.push_back(asio::buffer(&header, sizeof(rest_rpc_header)));
if constexpr (sizeof...(Args) > 0) {
buffers.push_back(asio::buffer(buf.data(), buf.size()));
}
using R = typename function_traits<decltype(func)>::return_type;
call_result<R> 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<R>(body_.data() + 1, resp_header.body_len - 1);
if constexpr (!std::is_void_v<R>) {
result.value = codec.unpack<R>(
std::string_view(body_.data() + 1, resp_header.body_len - 1));
}
co_return result;
}
+35 -1
View File
@@ -1,20 +1,37 @@
#ifndef REST_RPC_CODEC_H_
#define REST_RPC_CODEC_H_
#include "traits.h"
#include <charconv>
#include <msgpack.hpp>
namespace rest_rpc {
namespace rpc_service {
template <typename Arg> auto pack_one(Arg &&arg) {
if constexpr (util::CharArrayRef<Arg> || util::CharArray<Arg> ||
util::string<Arg>) {
return std::string_view(std::forward<Arg>(arg));
} else {
return std::to_string(arg);
}
}
using buffer_type = msgpack::sbuffer;
struct msgpack_codec {
const static size_t init_size = 2 * 1024;
template <typename... Args> static buffer_type pack_args(Args &&...args) {
template <typename... Args> 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<Args...>) {
return pack_one(std::forward<Args>(args)...);
} else {
buffer_type buffer(init_size);
msgpack::pack(buffer, std::forward_as_tuple(std::forward<Args>(args)...));
return buffer;
}
}
template <typename Arg> static std::string pack_to_string(Arg &arg) {
buffer_type buffer(init_size);
@@ -46,6 +63,23 @@ struct msgpack_codec {
}
}
template <typename T> T unpack(std::string_view data) {
if constexpr (std::is_fundamental_v<T>) {
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<std::string, T>) {
return std::string(data);
} else if constexpr (std::is_same_v<std::string_view, T>) {
return data;
} else {
return unpack<T>(data.data(), data.size());
}
}
private:
msgpack::unpacked msg_;
};
+2 -1
View File
@@ -79,7 +79,8 @@ auto apply_helper(F &&f, Tuple &&tp, nonstd::index_sequence<Idx...>)
}
template <typename F, typename Tuple>
auto apply(F &&f, Tuple &&tp) -> decltype(apply_helper(
auto apply(F &&f, Tuple &&tp)
-> decltype(apply_helper(
std::forward<F>(f), std::forward<Tuple>(tp),
make_index_sequence<std::tuple_size<decay_t<Tuple>>::value>{})) {
return apply_helper(
+5 -3
View File
@@ -28,7 +28,7 @@ struct function_traits<Ret(Arg, Args...)> {
public:
enum { arity = sizeof...(Args) + 1 };
typedef Ret function_type(Arg, Args...);
typedef Ret return_type;
typedef std::remove_cvref_t<Ret> return_type;
using stl_function_type = std::function<function_type>;
typedef Ret (*pointer)(Arg, Args...);
@@ -51,7 +51,7 @@ template <typename Ret> struct function_traits<Ret()> {
public:
enum { arity = 0 };
typedef Ret function_type();
typedef Ret return_type;
typedef std::remove_cvref_t<Ret> return_type;
using stl_function_type = std::function<function_type>;
typedef Ret (*pointer)();
@@ -123,7 +123,9 @@ using nth_type_of = nonstd::tuple_element_t<N, std::tuple<Args...>>;
template <typename... Args>
using last_type_of = nth_type_of<sizeof...(Args) - 1, Args...>;
template <typename T> struct remove_first { using type = T; };
template <typename T> struct remove_first {
using type = T;
};
template <class First, class... Second>
struct remove_first<std::tuple<First, Second...>> {
+11 -5
View File
@@ -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 <class T> struct add_const { typedef const T type; };
template <class T> struct add_const {
typedef const T type;
};
#endif // any_HAVE_ADD_CONST
@@ -401,8 +403,12 @@ using std::tr1::remove_reference;
#else
template <class T> struct remove_reference { typedef T type; };
template <class T> struct remove_reference<T &> { typedef T type; };
template <class T> struct remove_reference {
typedef T type;
};
template <class T> struct remove_reference<T &> {
typedef T type;
};
#endif // any_HAVE_REMOVE_REFERENCE
+8
View File
@@ -6,6 +6,14 @@
#include <arpa/inet.h>
#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 {
+7 -2
View File
@@ -32,24 +32,29 @@ public:
}
detail::resize(body_, header.body_len);
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<asio::const_buffer> 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) {
+82 -34
View File
@@ -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 <cstdint>
#include <functional>
#include <string>
@@ -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 <auto func> constexpr uint32_t get_key() {
constexpr auto name = get_func_name<func>();
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 ")
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,38 +100,86 @@ private:
template <typename Function>
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<Function>::tuple_type;
using R = typename function_traits<Function>::return_type;
rpc_service::msgpack_codec codec;
try {
auto tp = codec.unpack<args_tuple>(str.data(), str.size());
if constexpr (std::tuple_size_v<args_tuple> == 0) {
if constexpr (std::is_void_v<R>) {
f();
} else {
auto r = f();
ret.result = rpc_service::msgpack_codec::pack_to_string(r);
}
} else {
rpc_service::msgpack_codec codec;
using first_t = std::tuple_element_t<0, args_tuple>;
if constexpr (std::tuple_size_v<args_tuple> == 1 &&
util::is_basic_v<first_t>) {
if constexpr (std::is_void_v<R>) {
f(codec.unpack<first_t>(str));
} else {
if constexpr (std::is_same_v<std::string_view, R>) {
ret.view = rpc_service::msgpack_codec::pack_args(
f(codec.unpack<first_t>(str)));
} else {
ret.result = rpc_service::msgpack_codec::pack_args(
f(codec.unpack<first_t>(str)));
}
}
} else {
auto tp = codec.unpack<args_tuple>(str);
if constexpr (std::is_void_v<R>) {
std::apply(f, tp);
} else {
auto r = std::apply(f, tp);
result = rpc_service::msgpack_codec::pack_to_string(r);
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 <typename Function, typename Self>
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<Function>::tuple_type;
using R = typename function_traits<Function>::return_type;
rpc_service::msgpack_codec codec;
try {
auto tp = codec.unpack<args_tuple>(str.data(), str.size());
if constexpr (std::tuple_size_v<args_tuple> == 0) {
if constexpr (std::is_void_v<R>) {
(*self.*f)();
} else {
auto r = (*self.*f)();
ret.result = rpc_service::msgpack_codec::pack_to_string(r);
}
} else {
using first_t = std::tuple_element_t<0, args_tuple>;
if constexpr (std::tuple_size_v<args_tuple> == 1 &&
util::is_basic_v<first_t>) {
if constexpr (std::is_void_v<R>) {
(*self.*f)(codec.unpack<first_t>(str));
} else {
if constexpr (std::is_same_v<std::string_view, R>) {
ret.view = rpc_service::msgpack_codec::pack_args(
(*self.*f)(codec.unpack<first_t>(str)));
} else {
ret.result = rpc_service::msgpack_codec::pack_args(
(*self.*f)(codec.unpack<first_t>(str)));
}
}
} else {
auto tp = codec.unpack<args_tuple>(str);
if constexpr (std::is_void_v<R>) {
std::apply(
@@ -148,20 +193,23 @@ private:
return (*self.*f)(std::forward<decltype(args)>(args)...);
},
tp);
result = rpc_service::msgpack_codec::pack_to_string(r);
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<uint32_t, std::function<void(std::string_view, rpc_errc &,
std::string &)>>
std::unordered_map<uint32_t,
std::function<void(std::string_view, rpc_result &)>>
map_invokers_;
std::unordered_map<uint32_t, std::string> key2func_name_;
};
+43 -1
View File
@@ -145,7 +145,9 @@ struct is_invocable
template <typename F, typename... Args>
inline constexpr bool is_invocable_v = is_invocable<F, Args...>::value;
template <typename T> struct remove_first { using type = T; };
template <typename T> struct remove_first {
using type = T;
};
template <class First, class... Second>
struct remove_first<std::tuple<First, Second...>> {
@@ -171,4 +173,44 @@ struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <typename Test, template <typename...> class Ref>
inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
template <typename Type>
concept container = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
};
template <typename Type>
constexpr bool is_char_t =
std::is_same_v<Type, signed char> || std::is_same_v<Type, char> ||
std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> ||
std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t>
#ifdef __cpp_lib_char8_t
|| std::is_same_v<Type, char8_t>
#endif
;
template <typename Type>
concept string = container<Type> && requires(Type container) {
requires is_char_t<typename std::remove_cvref_t<Type>::value_type>;
container.length();
container.data();
};
template <typename T>
concept CharArrayRef = requires {
requires std::is_array_v<std::remove_reference_t<T>> &&
std::same_as<std::remove_extent_t<std::remove_cvref_t<T>>, char>;
};
template <typename T>
concept CharArray =
std::is_array_v<std::remove_reference_t<T>> &&
std::same_as<std::remove_extent_t<std::remove_cvref_t<T>>, char>;
template <typename T, typename...>
inline constexpr bool is_basic_v =
std::is_fundamental_v<T> || string<T> || CharArray<T> || CharArrayRef<T>;
} // namespace rest_rpc::util
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "function_name.h"
#include "md5.hpp"
namespace rest_rpc {
template <auto func> constexpr uint32_t get_key() {
constexpr auto name = get_func_name<func>();
constexpr uint32_t key = MD5::MD5Hash32(name.data(), name.length());
return key;
}
} // namespace rest_rpc
+56
View File
@@ -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<add>();
router.register_handler<foo>();
router.register_handler<round1>();
router.register_handler<echo>();
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<round1>(), s);
auto r1 = router.route(get_key<echo>(), 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<add>();
server.register_handler<foo>();
server.register_handler<no_arg>();
server.register_handler<echo>();
server.register_handler<echo_sv>();
server.register_handler<round1>();
auto ec = server.async_start();
CHECK(!ec);
client cl;
static_assert(util::CharArrayRef<char const(&)[5]>);
static_assert(util::CharArray<const char[5]>);
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<add>(1, 2),
// asio::use_future); auto result = future1.get();
{
// auto result = sync_wait(cl.get_executor(),
// cl.call_for<no_arg>(std::chrono::minutes(2)));
// CHECK(result.ec==rpc_errc::ok);
auto result =
sync_wait(cl.get_executor(),
cl.call_for<echo_sv>(std::chrono::minutes(2), "test"));
CHECK(result.ec == rpc_errc::ok);
auto result1 = sync_wait(
cl.get_executor(), cl.call_for<echo>(std::chrono::minutes(2), "test"));
CHECK(result1.ec == rpc_errc::ok);
auto result2 = sync_wait(cl.get_executor(), cl.call<round1>(1));
CHECK(result2.ec == rpc_errc::ok);
}
auto result = sync_wait(cl.get_executor(), cl.call<add>(1, 2));
CHECK(result.ec == rpc_errc::ok);
ec = server.async_start();
CHECK(!ec);