/* * Copyright (c) 2017-2023 zhllxt * * author : zhllxt * email : 37792738@qq.com * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef __ASIO2_RPC_PROTOCOL_HPP__ #define __ASIO2_RPC_PROTOCOL_HPP__ #if defined(_MSC_VER) && (_MSC_VER >= 1200) #pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include namespace asio2::detail { struct use_tcp {}; struct use_websocket {}; /* * request : message type + request id + function name + parameters value... * response : message type + request id + function name + error code + result value * * message type : q - request, p - response * * if result type is void, then result type will wrapped to std::int8_t */ static constexpr char rpc_type_req = 'q'; static constexpr char rpc_type_rep = 'p'; class rpc_header { public: using id_type = std::uint64_t; rpc_header() noexcept {} rpc_header(char type, id_type id, std::string_view name) : type_(type), id_(id), name_(name) {} ~rpc_header() = default; rpc_header(const rpc_header& r) : type_(r.type_), id_(r.id_), name_(r.name_) {} rpc_header(rpc_header&& r) noexcept : type_(r.type_), id_(r.id_), name_(std::move(r.name_)) {} inline rpc_header& operator=(const rpc_header& r) { type_ = r.type_; id_ = r.id_; name_ = r.name_; return (*this); } inline rpc_header& operator=(rpc_header&& r) noexcept { type_ = r.type_; id_ = r.id_; name_ = std::move(r.name_); return (*this); } //template //inline void serialize(Archive & ar) //{ // ar(type_, id_, name_); //} template void save(Archive & ar) const { ar(type_, id_, name_); } template void load(Archive & ar) { ar(type_, id_, name_); } inline char type() const noexcept { return this->type_; } inline id_type id () const noexcept { return this->id_; } inline const std::string& name() const noexcept { return this->name_; } inline char get_type() const noexcept { return this->type_; } inline id_type get_id () const noexcept { return this->id_; } inline const std::string& get_name() const noexcept { return this->name_; } inline bool is_request () const noexcept { return this->type_ == rpc_type_req; } inline bool is_response() const noexcept { return this->type_ == rpc_type_rep; } inline rpc_header& type(char type ) noexcept { this->type_ = type; return (*this); } inline rpc_header& id (id_type id ) noexcept { this->id_ = id ; return (*this); } inline rpc_header& name(std::string_view name) { this->name_ = name; return (*this); } inline rpc_header& set_type(char type ) noexcept { this->type_ = type; return (*this); } inline rpc_header& set_id (id_type id ) noexcept { this->id_ = id ; return (*this); } inline rpc_header& set_name(std::string_view name) { this->name_ = name; return (*this); } protected: char type_; id_type id_ = 0; std::string name_; }; template class rpc_request : public rpc_header { protected: template struct value_t { using type = T; }; template struct value_t> { using type = typename std::basic_string_view::value_type; }; template struct result_t { // if the parameters of rpc calling is raw pointer like char* , must convert it to std::string // if the parameters of rpc calling is std::string_view , must convert it to std::string // if the parameters of rpc calling is reference like std::string& , must remove it's // reference to std::string using ncvr_type = std::remove_cv_t>; using char_type = std::remove_cv_t>>>; using type = std::conditional_t< (std::is_pointer_v || std::is_array_v) && ( std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) , std::basic_string , std::conditional_t , std::basic_string::type> , ncvr_type>>; }; public: rpc_request() noexcept : rpc_header() { this->type_ = rpc_type_req; } // can't use tp_(std::forward_as_tuple(std::forward(args)...)) // if use tp_(std::forward_as_tuple(std::forward(args)...)), // when the args is nlohmann::json and under gcc 9.4.0, the json::object // maybe changed to json::array, like this: // {"name":"hello","age":10} will changed to [{"name":"hello","age":10}] // i don't why? rpc_request(std::string_view name, Args&&... args) : rpc_header(rpc_type_req, 0, name), tp_(std::forward(args)...) {} rpc_request(id_type id, std::string_view name, Args&&... args) : rpc_header(rpc_type_req, id, name), tp_(std::forward(args)...) {} ~rpc_request() = default; rpc_request(const rpc_request& r) : rpc_header(r), tp_(r.tp_) {} rpc_request(rpc_request&& r) noexcept : rpc_header(std::move(r)), tp_(std::move(r.tp_)) {} inline rpc_request& operator=(const rpc_request& r) { static_cast(*this) = r; tp_ = r.tp_; return (*this); } inline rpc_request& operator=(rpc_request&& r) noexcept { static_cast(*this) = std::move(r); tp_ = std::move(r.tp_); return (*this); } //template //void serialize(Archive & ar) //{ // ar(cereal::base_class(this)); // detail::for_each_tuple(tp_, [&ar](auto& elem) mutable // { // ar(elem); // }); //} template void save(Archive & ar) const { ar(cereal::base_class(this)); detail::for_each_tuple(tp_, [&ar](const auto& elem) mutable { ar << elem; }); } template void load(Archive & ar) { ar(cereal::base_class(this)); detail::for_each_tuple(tp_, [&ar](auto& elem) mutable { ar >> elem; }); } protected: std::tuple::type...> tp_; }; template class rpc_response : public rpc_header { public: rpc_response() noexcept : rpc_header() { this->type_ = rpc_type_rep; } rpc_response(id_type id, std::string_view name) : rpc_header(rpc_type_rep, id, name) {} rpc_response(id_type id, std::string_view name, const error_code& ec, T&& ret) : rpc_header(rpc_type_rep, id, name), ec_(ec), ret_(std::forward(ret)) {} ~rpc_response() = default; rpc_response(const rpc_response& r) : rpc_header(r), ec_(r.ec_), ret_(r.ret_) {} rpc_response(rpc_response&& r) noexcept : rpc_header(std::move(r)), ec_(std::move(r.ec_)), ret_(std::move(r.ret_)) {} inline rpc_response& operator=(const rpc_response& r) { static_cast(*this) = r; ec_ = r.ec_; ret_ = r.ret_; return (*this); } inline rpc_response& operator=(rpc_response&& r) noexcept { static_cast(*this) = std::move(r); ec_ = std::move(r.ec_); ret_ = std::move(r.ret_); return (*this); } //template //void serialize(Archive & ar) //{ // ar(cereal::base_class(this)); // ar(ec_.value()); // ar(ret_); //} template void save(Archive & ar) const { ar << cereal::base_class(this); ar << ec_.value(); ar << ret_; } template void load(Archive & ar) { ar >> cereal::base_class(this); ar >> ec_.value(); ar >> ret_; } protected: error_code ec_; T ret_; }; } #endif // !__ASIO2_RPC_PROTOCOL_HPP__