#ifndef _HPROSE_CLI_HPP_ #define _HPROSE_CLI_HPP_ #include #include #include #include "hprose_ed.hpp" #include "hprose_types.hpp" template struct HproseRPCCallback { template void CallImpl(HproseReader& reader, T rcb, const std::function& arg_ref) { auto vals = HproseValueHelperTuple().FromReader(reader); arg_ref(); CallImplMultiArg(rcb, vals, std::make_index_sequence{}); } template void CallImplMultiArg(T rcb, std::tuple& vals, std::index_sequence) { rcb(std::get(vals)...); } }; template struct HproseRPCCallback { template void CallImpl(HproseReader& reader, T rcb, const std::function& arg_ref) { auto val = HproseValueHelper().FromReader(reader); arg_ref(); rcb(val); } }; template<> struct HproseRPCCallback { template void CallImpl(HproseReader& reader, T rcb, const std::function& arg_ref) { reader.SkipValue(); arg_ref(); rcb(); } }; template struct HproseResultArg { using type = void; }; template struct HproseResultArg> { using type = HproseRPCCallback; }; template struct HproseResultArg> { using type = HproseRPCCallback>...>; }; class HproseRPCInfo { public: HproseRPCInfo(const std::string& name) : method(name) {} template HproseRPCInfo& PushArgs(ARGS... args) { arg_cb = [args = std::make_tuple(args...)](HproseWriter& writer) { HproseValueHelper>().ToWriter(args, writer); }; return *this; } template HproseRPCInfo& PushRefArgs(ARGS&... args) { ref_cb = [argrefs = std::tie(std::forward(args)...)](HproseReader& reader) mutable { argrefs = std::move(HproseValueHelperTuple().FromReader(reader)); }; ref = true; return PushArgs(std::forward(args)...); } template HproseRPCInfo& Expect(T rcb) { res_cb = [rcb](HproseReader& reader, const std::function& arg_ref) { using rpc_arg_type = HproseResultArg::type>; typename rpc_arg_type::type().CallImpl(reader, rcb, arg_ref); }; return *this; } HproseRPCInfo& Catch(std::function ecb) { err_cb = ecb; return *this; } void WriteRequest(HproseWriter& writer) const { writer.WriteTag(Tags::Call); HproseValueHelperString().ToWriter(method, writer); if(arg_cb) arg_cb(writer); if(ref) writer.WriteTag(Tags::True); } void Return(HproseReader& reader) { auto tag = reader.ReadTag(); if(tag == Tags::Error) { auto msg = HproseValueHelperString().FromReader(reader); if(err_cb) err_cb(msg); } else { auto cb = [this, &reader]() { if(ref && ref_cb && reader.CheckTag(Tags::Argument, true)) ref_cb(reader); }; if(res_cb) res_cb(reader, cb); else { reader.SkipValue(); cb(); } } } void Error(const std::string& msg) { if(err_cb) err_cb(msg); } protected: bool ref = false; std::string method; std::function)> res_cb; std::function ref_cb; std::function arg_cb; std::function err_cb; }; class HproseClient { public: template void UseConnector(SVC& svc) { apply_handler = [&svc, this](uint32_t id, std::shared_ptr> rpcs) { if(rpcs == nullptr) { auto& rpcs = results[id]; svc.SendRequest(id, rpcs); } else svc.SendRequestNoReturn(id, rpcs); }; svc.SetPacketHandler([this](uint32_t id, HproseReader& reader) { auto iter = results.find(id); if(iter != results.end()) { for(auto& inf : iter->second) inf.Return(reader); } results.erase(iter); }); svc.SetErrorHandler([this](uint32_t id, const std::string& msg) { auto& infos = results[id]; for(auto& inf : infos) inf.Error(msg); results.erase(id); }); } HproseRPCInfo& Call(const std::string& name) { auto pre_multiid = multiid; multiid = 0; auto& ret = MultiCall(name); MultiApply(); multiid = pre_multiid; return ret; } HproseRPCInfo& MultiCall(const std::string& name) { if(multiid == 0) multiid = callid++; auto& infos = results[multiid]; infos.emplace_back(name); return infos.back(); } void MultiApply() { if(multiid == 0) return; apply_handler(multiid, nullptr); multiid = 0; } HproseRPCInfo& CallNoReturn(const std::string& name) { auto rpcinfos = std::make_shared>(); rpcinfos->emplace_back(name); apply_handler(callid++, rpcinfos); return rpcinfos->at(0); } protected: uint32_t callid = 1; uint32_t multiid = 0; std::function>)> apply_handler; std::unordered_map> results; }; #endif