custom codec

This commit is contained in:
qicosmos
2025-10-13 16:02:20 +08:00
parent 0142d19bfc
commit 418cb2d67a
6 changed files with 111 additions and 50 deletions
+41 -11
View File
@@ -5,18 +5,44 @@
#include <charconv> #include <charconv>
#include <msgpack.hpp> #include <msgpack.hpp>
namespace rest_rpc { namespace user_codec {
struct rest_adl_tag {};
} // namespace user_codec
struct msgpack_codec { namespace rest_rpc {
namespace detail {
template <typename T, typename... Args>
struct has_user_pack : std::false_type {};
template <typename... Args>
struct has_user_pack<
std::void_t<
// AdlTag{} trigger ADL lookup user_codec namespace
decltype(serialize(std::declval<user_codec::rest_adl_tag>(),
std::declval<Args>()...))>,
Args...> : std::true_type {};
template <typename... Args>
inline constexpr bool has_user_pack_v = has_user_pack<void, Args...>::value;
} // namespace detail
struct rpc_codec {
template <typename... Args> inline static auto pack_args(Args &&...args) { template <typename... Args> inline static auto pack_args(Args &&...args) {
if constexpr (sizeof...(Args) == 0) { if constexpr (sizeof...(Args) == 0) {
return std::string_view{}; return std::string_view{};
} else if constexpr (sizeof...(Args) == 1 && util::is_basic_v<Args...>) { } else if constexpr (sizeof...(Args) == 1 && util::is_basic_v<Args...>) {
return pack_one(std::forward<Args>(args)...); return pack_one(std::forward<Args>(args)...);
} else { } else {
msgpack::sbuffer buffer(2 * 1024); if constexpr (detail::has_user_pack_v<Args...>) {
msgpack::pack(buffer, std::forward_as_tuple(std::forward<Args>(args)...)); return serialize(user_codec::rest_adl_tag{},
return std::string(buffer.data(), buffer.size()); std::forward<Args>(args)...);
} else {
msgpack::sbuffer buffer(2 * 1024);
msgpack::pack(buffer,
std::forward_as_tuple(std::forward<Args>(args)...));
return std::string(buffer.data(), buffer.size());
}
} }
} }
@@ -33,12 +59,16 @@ struct msgpack_codec {
} else if constexpr (std::is_same_v<std::string_view, T>) { } else if constexpr (std::is_same_v<std::string_view, T>) {
return data; return data;
} else { } else {
try { if constexpr (detail::has_user_pack_v<T>) {
static msgpack::unpacked msg; return deserialize<T>(user_codec::rest_adl_tag{}, data);
msgpack::unpack(msg, data.data(), data.size()); } else {
return msg.get().as<T>(); try {
} catch (...) { static msgpack::unpacked msg;
throw std::invalid_argument("unpack failed: Args not match!"); msgpack::unpack(msg, data.data(), data.size());
return msg.get().as<T>();
} catch (...) {
throw std::invalid_argument("unpack failed: Args not match!");
}
} }
} }
} }
+3 -3
View File
@@ -164,7 +164,7 @@ private:
template <typename R, typename... Args> template <typename R, typename... Args>
asio::awaitable<call_result<R>> call_impl(rest_rpc_header &header, asio::awaitable<call_result<R>> call_impl(rest_rpc_header &header,
Args &&...args) { Args &&...args) {
auto buf = msgpack_codec::pack_args(std::forward<Args>(args)...); auto buf = rpc_codec::pack_args(std::forward<Args>(args)...);
header.body_len = buf.size(); header.body_len = buf.size();
if (cross_ending_) { if (cross_ending_) {
prepare_for_send(header); prepare_for_send(header);
@@ -227,10 +227,10 @@ private:
result.ec = (rpc_errc)socket_->body_[0]; result.ec = (rpc_errc)socket_->body_[0];
if constexpr (!std::is_void_v<R>) { if constexpr (!std::is_void_v<R>) {
if constexpr (util::is_basic_v<R>) { if constexpr (util::is_basic_v<R>) {
result.value = msgpack_codec::unpack<R>(std::string_view( result.value = rpc_codec::unpack<R>(std::string_view(
socket_->body_.data() + 1, resp_header.body_len - 1)); socket_->body_.data() + 1, resp_header.body_len - 1));
} else { } else {
auto tp = msgpack_codec::unpack<std::tuple<R>>(std::string_view( auto tp = rpc_codec::unpack<std::tuple<R>>(std::string_view(
socket_->body_.data() + 1, resp_header.body_len - 1)); socket_->body_.data() + 1, resp_header.body_len - 1));
result.value = std::move(std::get<0>(tp)); result.value = std::move(std::get<0>(tp));
} }
+1 -1
View File
@@ -238,7 +238,7 @@ asio::awaitable<std::error_code> rpc_context::response(Args &&...args) {
co_return make_error_code(rpc_errc::rpc_context_init_failed); co_return make_error_code(rpc_errc::rpc_context_init_failed);
} }
rpc_result result(msgpack_codec::pack_args(std::forward<Args>(args)...)); rpc_result result(rpc_codec::pack_args(std::forward<Args>(args)...));
has_response_ = true; has_response_ = true;
co_return co_await conn_->response(result); co_return co_await conn_->response(result);
} }
+18 -20
View File
@@ -148,15 +148,15 @@ private:
} else { } else {
if constexpr (std::is_void_v<Self>) { if constexpr (std::is_void_v<Self>) {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args(co_await f()); ret = rpc_codec::pack_args(co_await f());
} else { } else {
ret = msgpack_codec::pack_args(f()); ret = rpc_codec::pack_args(f());
} }
} else { } else {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args(co_await (*self.*f)()); ret = rpc_codec::pack_args(co_await (*self.*f)());
} else { } else {
ret = msgpack_codec::pack_args((*self.*f)()); ret = rpc_codec::pack_args((*self.*f)());
} }
} }
} }
@@ -169,32 +169,30 @@ private:
if constexpr (is_void_v<R>) { if constexpr (is_void_v<R>) {
if constexpr (std::is_void_v<Self>) { if constexpr (std::is_void_v<Self>) {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
co_await f(msgpack_codec::unpack<Arg>(str)); co_await f(rpc_codec::unpack<Arg>(str));
} else { } else {
f(msgpack_codec::unpack<Arg>(str)); f(rpc_codec::unpack<Arg>(str));
} }
} else { } else {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
co_await (*self.*f)(msgpack_codec::unpack<Arg>(str)); co_await (*self.*f)(rpc_codec::unpack<Arg>(str));
} else { } else {
(*self.*f)(msgpack_codec::unpack<Arg>(str)); (*self.*f)(rpc_codec::unpack<Arg>(str));
} }
} }
} else { } else {
if constexpr (std::is_void_v<Self>) { if constexpr (std::is_void_v<Self>) {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args( ret = rpc_codec::pack_args(co_await f(rpc_codec::unpack<Arg>(str)));
co_await f(msgpack_codec::unpack<Arg>(str)));
} else { } else {
ret = msgpack_codec::pack_args(f(msgpack_codec::unpack<Arg>(str))); ret = rpc_codec::pack_args(f(rpc_codec::unpack<Arg>(str)));
} }
} else { } else {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args( ret = rpc_codec::pack_args(
co_await (*self.*f)(msgpack_codec::unpack<Arg>(str))); co_await (*self.*f)(rpc_codec::unpack<Arg>(str)));
} else { } else {
ret = msgpack_codec::pack_args( ret = rpc_codec::pack_args((*self.*f)(rpc_codec::unpack<Arg>(str)));
(*self.*f)(msgpack_codec::unpack<Arg>(str)));
} }
} }
} }
@@ -204,7 +202,7 @@ private:
template <typename R, typename Args, typename F, typename Self> template <typename R, typename Args, typename F, typename Self>
asio::awaitable<void> handle_more_args(std::string_view str, const F &f, asio::awaitable<void> handle_more_args(std::string_view str, const F &f,
rpc_result &ret, Self *self) { rpc_result &ret, Self *self) {
auto tp = msgpack_codec::unpack<Args>(str); auto tp = rpc_codec::unpack<Args>(str);
if constexpr (std::is_void_v<R>) { if constexpr (std::is_void_v<R>) {
if constexpr (std::is_void_v<Self>) { if constexpr (std::is_void_v<Self>) {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
@@ -230,19 +228,19 @@ private:
} else { } else {
if constexpr (std::is_void_v<Self>) { if constexpr (std::is_void_v<Self>) {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args(co_await std::apply(f, tp)); ret = rpc_codec::pack_args(co_await std::apply(f, tp));
} else { } else {
ret = msgpack_codec::pack_args(std::apply(f, tp)); ret = rpc_codec::pack_args(std::apply(f, tp));
} }
} else { } else {
if constexpr (is_awaitable_v<R>) { if constexpr (is_awaitable_v<R>) {
ret = msgpack_codec::pack_args(co_await std::apply( ret = rpc_codec::pack_args(co_await std::apply(
[self, &f](auto &&...args) { [self, &f](auto &&...args) {
return (*self.*f)(std::forward<decltype(args)>(args)...); return (*self.*f)(std::forward<decltype(args)>(args)...);
}, },
tp)); tp));
} else { } else {
ret = msgpack_codec::pack_args(std::apply( ret = rpc_codec::pack_args(std::apply(
[self, &f](auto &&...args) { [self, &f](auto &&...args) {
return (*self.*f)(std::forward<decltype(args)>(args)...); return (*self.*f)(std::forward<decltype(args)>(args)...);
}, },
+1 -2
View File
@@ -113,8 +113,7 @@ public:
auto conns = get_connections(); auto conns = get_connections();
for (auto &[_, conn] : conns) { for (auto &[_, conn] : conns) {
if (conn->topic_id() == id) { if (conn->topic_id() == id) {
co_await conn->response(msgpack_codec::pack_args(std::forward<T>(t)), co_await conn->response(rpc_codec::pack_args(std::forward<T>(t)), id);
id);
} }
} }
} }
+47 -13
View File
@@ -243,8 +243,8 @@ asio::awaitable<void> test_router() {
} }
{ {
auto s = msgpack_codec::pack_args(1); auto s = rpc_codec::pack_args(1);
auto s1 = msgpack_codec::pack_args("test"); auto s1 = rpc_codec::pack_args("test");
auto r = co_await router.route(get_key<round1>(), s); auto r = co_await router.route(get_key<round1>(), s);
auto r1 = co_await router.route(get_key<echo>(), s1); auto r1 = co_await router.route(get_key<echo>(), s1);
@@ -253,7 +253,7 @@ asio::awaitable<void> test_router() {
std::cout << "\n"; std::cout << "\n";
} }
auto args = msgpack_codec::pack_args(1, 2); auto args = rpc_codec::pack_args(1, 2);
std::string_view str(args.data(), args.size()); std::string_view str(args.data(), args.size());
{ {
@@ -262,12 +262,12 @@ asio::awaitable<void> test_router() {
std::cout << "\n"; std::cout << "\n";
} }
auto args1 = msgpack_codec::pack_args("it is a test"); auto args1 = rpc_codec::pack_args("it is a test");
std::string_view str1(args1.data(), args1.size()); std::string_view str1(args1.data(), args1.size());
{ {
auto result = co_await router.route(get_key<&dummy::add>(), str); auto result = co_await router.route(get_key<&dummy::add>(), str);
auto r = msgpack_codec::unpack<int>(result.result); auto r = rpc_codec::unpack<int>(result.result);
auto result1 = co_await router.route(get_key<&dummy::foo>(), str1); auto result1 = co_await router.route(get_key<&dummy::foo>(), str1);
CHECK(r == 3); CHECK(r == 3);
CHECK(result1.ec == rpc_errc::ok); CHECK(result1.ec == rpc_errc::ok);
@@ -293,10 +293,10 @@ TEST_CASE("test rpc_connection") {
person p{1, "tom", 20}; person p{1, "tom", 20};
auto buf = msgpack_codec::pack_args(p); auto buf = rpc_codec::pack_args(p);
auto ret = sync_wait(get_global_executor(), auto ret = sync_wait(get_global_executor(),
router.route(get_key<get_person>(), buf)); router.route(get_key<get_person>(), buf));
auto tp = msgpack_codec::unpack<std::tuple<person>>(ret.data()); auto tp = rpc_codec::unpack<std::tuple<person>>(ret.data());
dummy d{}; dummy d{};
router.register_handler<&dummy::add>(&d); router.register_handler<&dummy::add>(&d);
auto conn = std::make_shared<rpc_connection>(std::move(socket), conn_id, auto conn = std::make_shared<rpc_connection>(std::move(socket), conn_id,
@@ -334,12 +334,12 @@ TEST_CASE("test server start") {
static_assert(util::CharArrayRef<char const(&)[5]>); static_assert(util::CharArrayRef<char const(&)[5]>);
static_assert(util::CharArray<const char[5]>); static_assert(util::CharArray<const char[5]>);
msgpack_codec::pack_args(); rpc_codec::pack_args();
msgpack_codec::pack_args(1, 2); rpc_codec::pack_args(1, 2);
auto s1 = msgpack_codec::pack_args("test"); auto s1 = rpc_codec::pack_args("test");
auto s2 = msgpack_codec::pack_args(std::string_view("test2")); auto s2 = rpc_codec::pack_args(std::string_view("test2"));
auto s3 = msgpack_codec::pack_args(std::string("test2")); auto s3 = rpc_codec::pack_args(std::string("test2"));
auto s5 = msgpack_codec::pack_args(123); auto s5 = rpc_codec::pack_args(123);
// auto future = asio::co_spawn(cl.get_executor(), // auto future = asio::co_spawn(cl.get_executor(),
// cl.connect("127.0.0.1:9005"), asio::use_future); auto conn_ec = // cl.connect("127.0.0.1:9005"), asio::use_future); auto conn_ec =
@@ -555,6 +555,40 @@ TEST_CASE("test server address") {
thd.join(); thd.join();
} }
bool in_user_pack = false;
bool in_user_unpack = false;
namespace user_codec {
// adl lookup in user_codec namespace
template <typename... Args>
std::string serialize(rest_adl_tag, Args &&...args) {
in_user_pack = true;
msgpack::sbuffer buffer(2 * 1024);
msgpack::pack(buffer, std::forward_as_tuple(std::forward<Args>(args)...));
return std::string(buffer.data(), buffer.size());
}
template <typename T> T deserialize(rest_adl_tag, std::string_view data) {
try {
in_user_unpack = true;
static msgpack::unpacked msg;
msgpack::unpack(msg, data.data(), data.size());
return msg.get().as<T>();
} catch (...) {
return T{};
}
}
} // namespace user_codec
TEST_CASE("test user codec") {
auto buf = rpc_codec::pack_args(
std::make_tuple<int, std::string, int>(1, "tom", 20));
CHECK(in_user_pack);
std::string_view str(buf.data(), buf.size());
rpc_codec::unpack<std::tuple<int, std::string, int>>(str);
CHECK(in_user_unpack);
}
// doctest comments // doctest comments
// 'function' : must be 'attribute' - see issue #182 // 'function' : must be 'attribute' - see issue #182
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)