This commit is contained in:
qicosmos
2025-09-20 08:12:32 +08:00
parent 0737fefead
commit bf1c8747a4
18 changed files with 1258 additions and 4 deletions
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "use_asio.hpp"
namespace rest_rpc {
inline auto async_start(auto executor, auto &&coro) {
using R = typename std::remove_cvref_t<decltype(coro)>::value_type;
static_assert(std::is_void_v<R>);
asio::co_spawn(executor, std::move(coro), asio::detached);
}
inline auto async_future(auto executor, auto &&coro) {
return asio::co_spawn(executor, std::move(coro), asio::use_future);
}
inline auto sync_wait(auto executor, auto &&coro) {
return async_future(executor, std::move(coro)).get();
}
inline auto async_start(auto executor, auto &&coro, auto callback) {
using R = typename std::remove_cvref_t<decltype(coro)>::value_type;
if constexpr (std::is_void_v<R>) {
asio::co_spawn(
executor, std::move(coro),
asio::any_completion_handler<void(std::exception_ptr)>(
[cb = std::move(callback)](std::exception_ptr e) { cb(e); }));
} else {
asio::co_spawn(executor, std::move(coro),
asio::any_completion_handler<void(std::exception_ptr, R)>(
[cb = std::move(callback)](std::exception_ptr e, R r) {
cb(e, std::move(r));
}));
}
}
} // namespace rest_rpc
+174
View File
@@ -0,0 +1,174 @@
#pragma once
#include "asio_util.hpp"
#include "codec.h"
#include "error_code.h"
#include "io_context_pool.hpp"
#include "logger.hpp"
#include "meta_util.hpp"
#include "rest_rpc_protocol.hpp"
#include "traits.h"
#include "use_asio.hpp"
#include <asio/experimental/awaitable_operators.hpp>
#include <asio/steady_timer.hpp>
using namespace asio::experimental::awaitable_operators;
namespace rest_rpc {
template <typename R> struct call_result {
rpc_errc ec;
R value;
};
class client {
public:
client() : socket_(get_global_executor()) {}
auto get_executor() { return socket_.get_executor(); }
asio::awaitable<std::error_code> connect(
std::string_view host, std::string_view port,
std::chrono::steady_clock::duration duration = std::chrono::seconds(5)) {
asio::ip::tcp::resolver resolver(socket_.get_executor());
auto r = co_await (watchdog(duration) ||
resolver.async_resolve(
host, port, asio::as_tuple(asio::use_awaitable)));
if (r.index() == 0) {
REST_LOG_ERROR << "resolve timeout";
co_return make_error_code(rpc_errc::resolve_timeout);
}
auto [ec, endpoints] = std::get<1>(r);
if (ec) {
REST_LOG_ERROR << "resolve failed";
co_return ec;
}
auto it = endpoints.begin();
if (it == endpoints.end()) {
REST_LOG_ERROR << "resolve failed";
co_return std::make_error_code(std::errc::bad_address);
}
auto endpoint = it->endpoint();
auto conn_r = co_await (
watchdog(duration) ||
socket_.async_connect(endpoint, asio::as_tuple(asio::use_awaitable)));
if (conn_r.index() == 0) {
REST_LOG_ERROR << "connect timeout";
co_return make_error_code(rpc_errc::connection_timeout);
}
auto [conn_ec] = std::get<1>(conn_r);
if (conn_ec) {
REST_LOG_ERROR << "connect failed";
co_return conn_ec;
}
co_return std::error_code{};
}
asio::awaitable<std::error_code> connect(
std::string_view address,
std::chrono::steady_clock::duration duration = std::chrono::seconds(5)) {
std::string_view host;
std::string_view port;
size_t pos = address.find(':');
if (pos != std::string::npos) {
host = address.substr(0, pos);
port = address.substr(pos + 1);
}
return connect(host, port, duration);
}
template <auto func, typename... Args>
asio::awaitable<
call_result<typename function_traits<decltype(func)>::return_type>>
call(Args &&...args) {
return call_for<func>(std::chrono::seconds(5), std::forward<Args>(args)...);
}
template <auto func, typename... Args>
asio::awaitable<
call_result<typename function_traits<decltype(func)>::return_type>>
call_for(auto duration, Args &&...args) {
using args_tuple = typename function_traits<decltype(func)>::tuple_type;
static_assert(std::is_constructible_v<args_tuple, Args...>,
"called rpc function and arguments are not match");
using R = typename function_traits<decltype(func)>::return_type;
auto r = co_await (watchdog(duration) ||
call_impl<func>(std::forward<Args>(args)...));
if (r.index() == 0) {
co_return call_result<R>{rpc_errc::request_timeout};
}
co_return std::get<1>(r);
}
private:
template <auto func, typename... Args>
asio::awaitable<
call_result<typename function_traits<decltype(func)>::return_type>>
call_impl(Args &&...args) {
rest_rpc_header header{};
header.magic = 39;
header.function_id = get_key<func>();
rpc_service::msgpack_codec codec;
auto buf = codec.pack_args(std::forward<Args>(args)...);
header.body_len = buf.size();
prepare_for_send(header);
std::vector<asio::const_buffer> buffers;
buffers.reserve(2);
buffers.push_back(asio::buffer(&header, sizeof(rest_rpc_header)));
buffers.push_back(asio::buffer(buf.data(), buf.size()));
using R = typename function_traits<decltype(func)>::return_type;
call_result<R> result{};
std::error_code ec;
size_t size;
std::tie(ec, size) = co_await asio::async_write(
socket_, buffers, asio::as_tuple(asio::use_awaitable));
if (ec) {
result.ec = rpc_errc::write_error;
co_return result;
}
rest_rpc_header resp_header;
std::tie(ec, size) = co_await asio::async_read(
socket_, asio::buffer(&resp_header, sizeof(rest_rpc_header)),
asio::as_tuple(asio::use_awaitable));
if (ec) {
result.ec = rpc_errc::write_error;
co_return result;
}
if (resp_header.magic != 39) {
result.ec = rpc_errc::protocol_error;
co_return result;
}
parse_recieved(resp_header);
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));
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);
co_return result;
}
asio::awaitable<std::error_code> watchdog(auto duration) {
asio::steady_timer timer(socket_.get_executor());
timer.expires_after(duration);
auto [ec] = co_await timer.async_wait(asio::as_tuple(asio::use_awaitable));
co_return ec;
}
tcp_socket socket_;
std::string body_;
};
} // namespace rest_rpc
+6
View File
@@ -16,6 +16,12 @@ struct msgpack_codec {
return buffer;
}
template <typename Arg> static std::string pack_to_string(Arg &arg) {
buffer_type buffer(init_size);
msgpack::pack(buffer, arg);
return std::string(buffer.data(), buffer.size());
}
template <typename Arg, typename... Args,
typename = typename std::enable_if<std::is_enum<Arg>::value>::type>
static std::string pack_args_str(Arg arg, Args &&...args) {
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#include <string>
#include <system_error>
namespace rest_rpc {
enum class rpc_errc : std::int8_t {
ok = 0,
no_such_function,
no_such_key,
invalid_req_type,
function_exception,
function_unknown_exception,
invalid_argument,
write_error,
read_error,
socket_closed,
resolve_timeout,
connection_timeout,
request_timeout,
protocol_error
};
class rpc_error_category : public std::error_category {
public:
const char *name() const noexcept override { return "rest_rpc_error"; }
std::string message(int ev) const override {
switch (static_cast<rpc_errc>(ev)) {
case rpc_errc::ok:
return "ok";
case rpc_errc::no_such_function:
return "no such function";
case rpc_errc::no_such_key:
return "resolve failed";
case rpc_errc::invalid_req_type:
return "invalid request type";
case rpc_errc::function_exception:
return "logic function exception happend";
case rpc_errc::function_unknown_exception:
return "unknown function exception happend";
case rpc_errc::invalid_argument:
return "invalid argument";
case rpc_errc::write_error:
return "write failed";
case rpc_errc::read_error:
return "read failed";
case rpc_errc::socket_closed:
return "socket closed";
case rpc_errc::resolve_timeout:
return "resolve timeout";
case rpc_errc::connection_timeout:
return "connect timeout";
case rpc_errc::request_timeout:
return "request timeout";
case rpc_errc::protocol_error:
return "protocol error";
default:
return "unknown error";
}
}
};
inline rest_rpc::rpc_error_category &category() {
static rest_rpc::rpc_error_category instance;
return instance;
}
inline std::error_code make_error_code(rpc_errc e) {
return {static_cast<int>(e), category()};
}
inline bool operator==(const std::error_code &code, rpc_errc ec) {
return code.value() == (int)ec;
}
} // namespace rest_rpc
+16
View File
@@ -45,6 +45,11 @@ public:
asio::io_context &get_io_context() { return *get_io_context_ptr(); }
auto get_executor() {
auto &ctx = get_io_context();
return ctx.get_executor();
}
private:
std::vector<std::shared_ptr<asio::io_context>> io_contexts_;
std::vector<asio::executor_work_guard<asio::io_context::executor_type>>
@@ -53,4 +58,15 @@ private:
std::once_flag stop_flag_;
std::atomic<size_t> next_ = 0;
};
inline auto
get_global_executor(unsigned pool_size = std::thread::hardware_concurrency()) {
static auto g_io_context_pool = std::make_shared<io_context_pool>(pool_size);
[[maybe_unused]] static bool run_helper = [](auto pool) {
std::thread thrd{[pool] { pool->run(); }};
thrd.detach();
return true;
}(g_io_context_pool);
return g_io_context_pool->get_executor();
}
} // namespace rest_rpc
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <iostream>
namespace rest_rpc {
struct null_logger_t {
template <typename T> const null_logger_t &operator<<(T &&) const {
return *this;
}
};
struct cout_logger_t {
template <typename T> const cout_logger_t &operator<<(T &&t) const {
std::cout << std::forward<T>(t);
return *this;
}
~cout_logger_t() { std::cout << std::endl; }
};
struct cerr_logger_t {
template <typename T> const cerr_logger_t &operator<<(T &&t) const {
std::cerr << std::forward<T>(t);
return *this;
}
~cerr_logger_t() { std::cerr << std::endl; }
};
constexpr inline rest_rpc::null_logger_t NULL_LOGGER;
} // namespace rest_rpc
#ifdef REST_LOG_ERROR
#else
#define REST_LOG_ERROR \
rest_rpc::cerr_logger_t {}
#endif
#ifdef REST_LOG_WARNING
#else
#ifndef NDEBUG
#define REST_LOG_WARNING \
rest_rpc::cerr_logger_t {}
#else
#define REST_LOG_WARNING rest_rpc::NULL_LOGGER
#endif
#endif
#ifdef REST_LOG_INFO
#else
#ifndef NDEBUG
#define REST_LOG_INFO \
rest_rpc::cout_logger_t {}
#else
#define REST_LOG_INFO rest_rpc::NULL_LOGGER
#endif
#endif
#ifdef REST_LOG_DEBUG
#else
#ifndef NDEBUG
#define REST_LOG_DEBUG \
rest_rpc::cout_logger_t {}
#else
#define REST_LOG_DEBUG rest_rpc::NULL_LOGGER
#endif
#endif
#ifdef REST_LOG_TRACE
#else
#ifndef NDEBUG
#define REST_LOG_TRACE \
rest_rpc::cout_logger_t {}
#else
#define REST_LOG_TRACE rest_rpc::NULL_LOGGER
#endif
#endif
+6 -2
View File
@@ -3,6 +3,7 @@
#include "cplusplus_14.h"
#include <functional>
#include <type_traits>
namespace rest_rpc {
@@ -31,12 +32,15 @@ public:
using stl_function_type = std::function<function_type>;
typedef Ret (*pointer)(Arg, Args...);
typedef std::tuple<Arg, Args...> tuple_type;
typedef std::tuple<std::remove_const_t<std::remove_reference_t<Arg>>,
std::remove_const_t<std::remove_reference_t<Args>>...>
tuple_type;
typedef std::tuple<
nonstd::remove_const_t<nonstd::remove_reference_t<Args>>...>
bare_tuple_type;
using args_tuple =
std::tuple<std::string, Arg,
std::tuple<std::string,
nonstd::remove_const_t<nonstd::remove_reference_t<Arg>>,
nonstd::remove_const_t<nonstd::remove_reference_t<Args>>...>;
using args_tuple_2nd =
std::tuple<std::string,
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
namespace rest_rpc {
inline constexpr uint8_t REST_MAGIC_NUM = 39;
struct rest_rpc_header {
uint8_t magic;
uint8_t version;
uint8_t serialize_type;
uint8_t msg_type;
uint32_t function_id;
uint64_t seq_num;
uint64_t body_len;
uint64_t attach_length;
};
inline void prepare_for_send(rest_rpc_header &header) {
header.function_id = htonl(header.function_id);
header.seq_num = htonll(header.seq_num);
header.body_len = htonll(header.body_len);
header.attach_length = htonll(header.attach_length);
}
inline void parse_recieved(rest_rpc_header &header) {
header.function_id = ntohl(header.function_id);
header.seq_num = ntohll(header.seq_num);
header.body_len = ntohll(header.body_len);
header.attach_length = ntohll(header.attach_length);
}
} // namespace rest_rpc
+173
View File
@@ -0,0 +1,173 @@
#pragma once
#include "io_context_pool.hpp"
#include "logger.hpp"
#include "rpc_connection.hpp"
#include "use_asio.hpp"
#include <string>
#include <thread>
namespace rest_rpc {
class rest_rpc_server {
public:
rest_rpc_server(std::string address,
size_t num_thread = std::thread::hardware_concurrency())
: io_context_pool_(num_thread),
acceptor_(io_context_pool_.get_io_context()) {
size_t pos = address.find(':');
if (pos != std::string::npos) {
host_ = address.substr(0, pos);
port_ = address.substr(pos + 1);
}
}
rest_rpc_server(std::string host, std::string port,
size_t num_thread = std::thread::hardware_concurrency())
: io_context_pool_(num_thread),
acceptor_(io_context_pool_.get_io_context()), host_(std::move(host)),
port_(std::move(port)) {}
~rest_rpc_server() { stop(); }
std::error_code start() { return start_impl(false); }
std::error_code async_start() { return start_impl(true); }
void stop() {
if (has_stop_.load(std::memory_order_acquire)) {
return;
}
std::call_once(stop_flag_, [this] {
has_stop_.store(true, std::memory_order_release);
asio::dispatch(acceptor_.get_executor(), [this]() {
asio::error_code ec;
(void)acceptor_.cancel(ec);
(void)acceptor_.close(ec);
});
io_context_pool_.stop();
});
if (thd_.joinable()) {
thd_.join();
}
}
bool has_stopped() const { return has_stop_.load(std::memory_order_acquire); }
template <typename Function, typename Self = void>
void register_handler(std::string_view name, const Function &f,
Self *self = nullptr) {
router_.register_handler(name, f, self);
}
template <auto func, typename Self = void>
void register_handler(Self *self = nullptr) {
router_.register_handler<func>(self);
}
void remove_handler(std::string_view name) { router_.remove_handler(name); }
template <auto func> void remove_handler() { router_.remove_handler<func>(); }
private:
std::error_code listen() {
using asio::ip::tcp;
asio::error_code ec;
asio::ip::tcp::resolver resolver(acceptor_.get_executor());
auto endpoints = resolver.resolve(host_, port_, ec);
if (ec) {
return ec;
}
auto it = endpoints.begin();
if (it == endpoints.end()) {
return std::make_error_code(std::errc::bad_address);
}
auto endpoint = it->endpoint();
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
return ec;
}
#ifdef __GNUC__
acceptor_.set_option(tcp::acceptor::reuse_address(true), ec);
#endif
acceptor_.bind(endpoint, ec);
if (ec) {
std::error_code ignore;
acceptor_.cancel(ignore);
acceptor_.close(ignore);
return ec;
}
#ifdef _MSC_VER
acceptor_.set_option(tcp::acceptor::reuse_address(true));
#endif
acceptor_.listen(asio::socket_base::max_listen_connections, ec);
if (ec) {
std::error_code ignore;
acceptor_.cancel(ignore);
acceptor_.close(ignore);
return ec;
}
return ec;
}
asio::awaitable<void> accept() {
uint64_t conn_id = 0;
while (true) {
tcp_socket socket(io_context_pool_.get_io_context());
auto [ec] = co_await acceptor_.async_accept(
socket, asio::as_tuple(asio::use_awaitable));
if (ec == asio::error::operation_aborted ||
ec == asio::error::bad_descriptor) {
REST_LOG_WARNING << "acceptor error: " << ec.message();
co_return;
}
REST_LOG_INFO << "new connction comming...";
auto conn =
std::make_shared<rpc_connection>(std::move(socket), conn_id, router_);
conns_.emplace(conn_id++, conn);
co_spawn(socket.get_executor(), conn->start(), asio::detached);
}
}
std::error_code start_impl(bool async) {
if (has_stop_.load(std::memory_order_acquire)) {
return std::make_error_code(std::errc::operation_canceled);
}
static std::error_code ec{};
std::call_once(start_flag_, [this, async] {
ec = listen();
if (ec) {
return;
}
thd_ = std::thread([this] { io_context_pool_.run(); });
if (async) {
asio::co_spawn(acceptor_.get_executor(), accept(), asio::detached);
} else {
auto future = asio::co_spawn(acceptor_.get_executor(), accept(),
asio::use_future);
future.wait();
}
});
return ec;
}
io_context_pool io_context_pool_;
std::thread thd_;
asio::ip::tcp::acceptor acceptor_;
std::string host_;
std::string port_;
std::once_flag start_flag_;
std::once_flag stop_flag_;
std::atomic<bool> has_stop_ = false;
std::unordered_map<uint64_t, std::shared_ptr<rpc_connection>> conns_;
rpc_router router_;
};
} // namespace rest_rpc
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include "logger.hpp"
#include "rest_rpc_protocol.hpp"
#include "rpc_router.hpp"
#include "string_resize.hpp"
#include "use_asio.hpp"
namespace rest_rpc {
class rpc_connection {
public:
rpc_connection(tcp_socket socket, uint64_t conn_id, rpc_router &router)
: socket_(std::move(socket)), conn_id_(conn_id), router_(router) {}
asio::awaitable<void> start() {
rest_rpc_header header;
while (true) {
std::error_code ec;
size_t size;
std::tie(ec, size) = co_await asio::async_read(
socket_, asio::buffer(&header, sizeof(rest_rpc_header)),
asio::as_tuple(asio::use_awaitable));
if (ec) {
REST_LOG_INFO << "read head error: " << ec.message();
break;
}
parse_recieved(header);
if (header.magic != REST_MAGIC_NUM) {
REST_LOG_ERROR << "protocol error";
break;
}
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;
}
// 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;
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));
std::tie(ec, size) = co_await asio::async_write(
socket_, buffers, asio::as_tuple(asio::use_awaitable));
if (ec) {
REST_LOG_WARNING << "write error: " << ec.message();
break;
}
}
co_return;
}
uint64_t id() const { return conn_id_; }
private:
tcp_socket socket_;
uint64_t conn_id_;
std::string body_;
rpc_router &router_;
};
} // namespace rest_rpc
+168
View File
@@ -0,0 +1,168 @@
#pragma once
#include "codec.h"
#include "error_code.h"
#include "function_name.h"
#include "md5.hpp"
#include "meta_util.hpp"
#include <cstdint>
#include <functional>
#include <string>
#include <string_view>
namespace rest_rpc {
struct rpc_result {
rpc_errc ec = rpc_errc::ok;
std::string result;
};
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;
}
class rpc_router {
public:
template <typename Function, typename Self = void>
void register_handler(std::string_view name, const Function &f,
Self *self = nullptr) {
uint32_t key = MD5::MD5Hash32(name.data(), name.length());
register_handler_impl(key, name, f, self);
}
template <auto func, typename Self = void>
void register_handler(Self *self = nullptr) {
constexpr auto name = get_func_name<func>();
return register_handler(name, func, self);
}
void remove_handler(std::string_view name) {
uint32_t key = MD5::MD5Hash32(name.data(), name.length());
this->map_invokers_.erase(key);
key2func_name_.erase(key);
}
template <auto func> void remove_handler() {
constexpr std::string_view name = get_func_name<func>();
remove_handler(name);
}
std::string get_name_by_key(uint32_t key) {
auto it = key2func_name_.find(key);
if (it != key2func_name_.end()) {
return it->second;
}
return std::to_string(key);
}
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.ec = rpc_errc::no_such_function;
} else {
it->second(data, route_result.ec, 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.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.ec = rpc_errc::function_unknown_exception;
}
route_result.result = std::move(result);
return route_result;
}
private:
template <typename Function, typename Self = void>
auto register_handler_impl(uint32_t key, std::string_view name,
const Function &f, Self *self = nullptr) {
if (key2func_name_.find(key) != key2func_name_.end()) {
throw std::invalid_argument("duplicate registration key !");
} else {
key2func_name_.emplace(key, name);
if constexpr (std::is_void_v<Self>) {
return register_nonmember_func(key, f);
} else {
return register_member_func(key, f, self);
}
}
}
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 {
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::is_void_v<R>) {
std::apply(f, tp);
} else {
auto r = std::apply(f, tp);
result = rpc_service::msgpack_codec::pack_to_string(r);
}
} catch (std::invalid_argument &e) {
ec = rpc_errc::invalid_argument;
result = e.what();
} catch (const std::exception &e) {
ec = rpc_errc::function_exception;
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) {
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::is_void_v<R>) {
std::apply(
[self, &f](auto &&...args) {
return (*self.*f)(std::forward<decltype(args)>(args)...);
},
tp);
} else {
auto r = std::apply(
[self, &f](auto &&...args) {
return (*self.*f)(std::forward<decltype(args)>(args)...);
},
tp);
result = rpc_service::msgpack_codec::pack_to_string(r);
}
} catch (std::invalid_argument &e) {
ec = rpc_errc::invalid_argument;
result = e.what();
} catch (const std::exception &e) {
ec = rpc_errc::function_exception;
result = e.what();
}
};
}
std::unordered_map<uint32_t, std::function<void(std::string_view, rpc_errc &,
std::string &)>>
map_invokers_;
std::unordered_map<uint32_t, std::string> key2func_name_;
};
} // namespace rest_rpc
+126
View File
@@ -0,0 +1,126 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
namespace rest_rpc::detail {
#if __cpp_lib_string_resize_and_overwrite >= 202110L
template <typename ch>
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) { return sz; });
}
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
// old msvc don't support visit private, discard it.
#else
template <typename Function, Function func_ptr> class string_thief {
public:
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mysize = sz;
#else
#if defined(_LIBCPP_VERSION)
(self.*func_ptr)(sz);
#else
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
#else
#if defined(__GLIBCXX__)
(self.*func_ptr)(sz);
#endif
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
#if (_GLIBCXX_USE_CXX11_ABI == 0)
template class string_thief<decltype(&std::string::_M_rep),
&std::string::_M_rep>;
#else
template class string_thief<decltype(&std::string::_M_set_length),
&std::string::_M_set_length>;
#endif
#elif defined(_LIBCPP_VERSION)
template class string_thief<decltype(&std::string::__set_size),
&std::string::__set_size>;
#elif defined(_MSVC_STL_VERSION)
template class string_thief<decltype(&std::string::_Mypair),
&std::string::_Mypair>;
#endif
void string_set_length_hacker(std::string &, std::size_t);
template <typename ch>
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
if (sz > str.capacity()) {
str.reserve(sz);
}
string_set_length_hacker(str, sz);
str[sz] = '\0';
#else
raw_str.resize(sz);
#endif
}
#endif
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
#else
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
template <typename Function, Function func_ptr> class vector_thief {
public:
friend void vector_set_length_hacker(std::vector<char> &self,
std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
#else
#if defined(_LIBCPP_VERSION)
#if _LIBCPP_VERSION < 14000
((*(std::__vector_base<char, std::allocator<char>> *)(&self)).*func_ptr) =
self.data() + sz;
#else
(self.*func_ptr) = self.data() + sz;
#endif
#else
#if defined(__GLIBCXX__)
((*(std::_Vector_base<char, std::allocator<char>> *)(&self)).*func_ptr)
._M_finish = self.data() + sz;
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
template class vector_thief<decltype(&std::vector<char>::_M_impl),
&std::vector<char>::_M_impl>;
#elif defined(_LIBCPP_VERSION)
template class vector_thief<decltype(&std::vector<char>::__end_),
&std::vector<char>::__end_>;
#elif defined(_MSVC_STL_VERSION)
template class vector_thief<decltype(&std::vector<char>::_Mypair),
&std::vector<char>::_Mypair>;
#endif
template <typename ch>
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
#if defined(__GLIBCXX__) || \
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
defined(_MSVC_STL_VERSION)
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
vec.reserve(sz);
vector_set_length_hacker(vec, sz);
#else
raw_vec.resize(sz);
#endif
}
#endif
}; // namespace rest_rpc::detail
+174
View File
@@ -0,0 +1,174 @@
#pragma once
#include <functional>
#include <memory>
namespace rest_rpc::util {
template <typename Function> struct function_traits;
template <typename Return, typename... Arguments>
struct function_traits<Return (*)(Arguments...)> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
};
template <typename Return, typename... Arguments>
struct function_traits<Return (*)(Arguments...) noexcept> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
};
template <typename Return, typename... Arguments>
struct function_traits<Return(Arguments...)> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
};
template <typename Return, typename... Arguments>
struct function_traits<Return(Arguments...) noexcept> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
};
template <typename This, typename Return, typename... Arguments>
struct function_traits<Return (This::*)(Arguments...)> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return, typename... Arguments>
struct function_traits<Return (This::*)(Arguments...) noexcept> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return, typename... Arguments>
struct function_traits<Return (This::*)(Arguments...) const> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return, typename... Arguments>
struct function_traits<Return (This::*)(Arguments...) const noexcept> {
using parameters_type = std::tuple<std::remove_cvref_t<Arguments>...>;
using return_type = Return;
using class_type = This;
};
template <typename Return> struct function_traits<Return (*)()> {
using parameters_type = void;
using return_type = Return;
};
template <typename Return> struct function_traits<Return (*)() noexcept> {
using parameters_type = void;
using return_type = Return;
};
template <typename Return> struct function_traits<Return (&)()> {
using parameters_type = void;
using return_type = Return;
};
template <typename Return> struct function_traits<Return (&)() noexcept> {
using parameters_type = void;
using return_type = Return;
};
template <typename Return> struct function_traits<Return()> {
using parameters_type = void;
using return_type = Return;
};
template <typename Return> struct function_traits<Return() noexcept> {
using parameters_type = void;
using return_type = Return;
};
template <typename This, typename Return>
struct function_traits<Return (This::*)()> {
using parameters_type = void;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return>
struct function_traits<Return (This::*)() noexcept> {
using parameters_type = void;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return>
struct function_traits<Return (This::*)() const> {
using parameters_type = void;
using return_type = Return;
using class_type = This;
};
template <typename This, typename Return>
struct function_traits<Return (This::*)() const noexcept> {
using parameters_type = void;
using return_type = Return;
using class_type = This;
};
// Support function object and lambda expression
template <class Function>
struct function_traits : function_traits<decltype(&Function::operator())> {};
template <typename Function>
using function_parameters_t =
typename function_traits<std::remove_cvref_t<Function>>::parameters_type;
template <typename Function>
using last_parameters_type_t =
std::tuple_element_t<std::tuple_size_v<function_parameters_t<Function>> - 1,
function_parameters_t<Function>>;
template <typename Function>
using function_return_type_t =
typename function_traits<std::remove_cvref_t<Function>>::return_type;
template <typename Function>
using class_type_t =
typename function_traits<std::remove_cvref_t<Function>>::class_type;
template <typename F, typename... Args>
struct is_invocable
: std::is_constructible<
std::function<void(std::remove_reference_t<Args>...)>,
std::reference_wrapper<typename std::remove_reference<F>::type>> {};
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 <class First, class... Second>
struct remove_first<std::tuple<First, Second...>> {
using type = std::tuple<Second...>;
};
template <typename T> using remove_first_t = typename remove_first<T>::type;
template <bool has_conn, typename T> inline auto get_args() {
if constexpr (has_conn) {
using args_type = remove_first_t<T>;
return args_type{};
} else {
return T{};
}
}
template <typename Test, template <typename...> class Ref>
struct is_specialization : std::false_type {};
template <template <typename...> class Ref, typename... Args>
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;
} // namespace rest_rpc::util
+3
View File
@@ -4,6 +4,9 @@
#ifdef CINATRA_ENABLE_SSL
#include <asio/ssl.hpp>
#endif
#include <asio/as_tuple.hpp>
#include <asio/co_spawn.hpp>
#include <asio/detached.hpp>
#include <asio/detail/noncopyable.hpp>
#include <asio/executor_work_guard.hpp>
#include <asio/post.hpp>