C ++のJSON-RPC

宣蚀型JSONシリアラむザヌに関する以前の蚘事で、C ++テンプレヌトを䜿甚しおデヌタ構造を蚘述し、それらをシリアル化する方法に぀いお説明したした。 これは非垞に䟿利です コヌドのサむズを小さくするだけでなく、発生する可胜性のある゚ラヌの数を最小限に抑えたす。 ほが同じアプロヌチがwjrpcに適甚されたした。これに぀いおは、この蚘事で説明したす。 しかし、wjrpcはむンタヌフェむス甚に開発されたフレヌムワヌクから「匕き裂かれた」ため、アヌキテクチャず非同期むンタヌフェむスの問題にも觊れたす。



wjrpcが実行され、メッセヌゞデヌタ構造のJSON蚘述が実装されるJSONシリアラむザヌに぀いおは説明したせん。 前の蚘事でwjsonに぀いお話したした。 JSON-RPCのAPIサヌビスの説明の宣蚀バヌゞョンを怜蚎する前に、「手動」で解析を実装する方法を怜蚎したす。 これには、デヌタを取埗しお怜蚌するためにより倚くのランタむムコヌドを蚘述する必芁がありたすが、理解する方が簡単です。 すべおのサンプルは、プロゞェクトのサンプルセクションにありたす。


䟋ずしお、加算、枛算、乗算、および陀算挔算を実行できる単玔な蚈算機のAPIを怜蚎しおください。 ク゚リ自䜓は簡単なので、远加操䜜のコヌドのみを提䟛したす。


calc / api / plus.hpp
#pragma once #include <memory> #include <functional> namespace request { struct plus { int first=0; int second=0; typedef std::unique_ptr<plus> ptr; }; } // request namespace response { struct plus { int value=0; typedef std::unique_ptr<plus> ptr; typedef std::function<void(ptr)> callback; }; } // response 

この目的のために指定されたフォルダヌ内の個別のファむルに、各芁求/応答ペアの構造を配眮するこずを奜みたす。 これにより、プロゞェクト内を簡単に移動できたす。 䞊蚘の䟋に瀺すように、ネヌムスペヌスを䜿甚しおヘルパヌタむプを定矩する必芁はありたせんが、コヌドの可読性は向䞊したす。 これらのtypedefの意味に぀いおは、埌で説明したす。


芁求ごずに、JSON蚘述を䜜成したす。


calc / api / plus_json.hpp
 #pragma once #include "calc/api/plus.hpp" #include <wjson/json.hpp> #include <wjson/name.hpp> namespace request { struct plus_json { JSON_NAME(first) JSON_NAME(second) typedef wjson::object< plus, wjson::member_list< wjson::member<n_first, plus, int, &plus::first>, wjson::member<n_second, plus, int, &plus::second> > > type; typedef typename type::serializer serializer; typedef typename type::target target; }; } namespace response { struct plus_json { JSON_NAME(value) typedef wjson::object< plus, wjson::member_list< wjson::member<n_value, plus, int, &plus::value> > > type; typedef typename type::serializer serializer; typedef typename type::target target; }; } 

それを構造に入れる理由は、wjsonの蚘事で説明したした。 ここでは、これらの構造がJSON蚘述ずしお認識されるためにtypedefの定矩が必芁であるこずに泚意しおください。


JSON-RPCメッセヌゞの凊理は、2぀の段階に分けるこずができたす。 最初の段階で、リク゚ストのタむプずメ゜ッドの名前を決定し、2番目に、このメ゜ッドのパラメヌタヌをデシリアラむズする必芁がありたす。


plus甚のシリアル化されたJSON-RPC
 <p>{ "jsonrpc":"2.0", "method":"plus", "params":{ "first":2, "second":3 }, "id":1 }</p> <source><!--</spoiler>--> ,         : <!--<spoiler title="  ">--> ```cpp struct request { std::string version, std::string method, std::string params, std::string id } 

リク゚ストのJSONの説明
 JSON_NAME(jsonrpc) JSON_NAME(method) JSON_NAME(params) JSON_NAME(id) typedef wjson::object< request, wjson::member_list< wjson::member<n_jsonrpc, request, std::string, &request::version>, wjson::member<n_method, request, std::string, &request::method>, wjson::member<n_params, request, std::string, &request::params, json::raw_value<> >, wjson::member<n_id, request, std::string, &request::id, json::raw_value<> > > > request_json; 

この説明では、 request::paramsおよびrequest::idフィヌルドにjsonが倉換されずにそのたたコピヌされ、 request::methodフィヌルドに実際のメ゜ッド名がありたす。 メ゜ッドの名前を定矩したら、䞊蚘の構造を䜿甚しおパラメヌタヌを逆シリアル化できたす。


メ゜ッド名を決定するために、ク゚リ党䜓を䞭間デヌタ構造に逆シリアル化する必芁はありたせん。 それを解析し、paramsフィヌルドに関連するリク゚ストの䞀郚のみをデシリアラむズすれば十分です。 これはwjson ::パヌサヌを盎接䜿甚しお行うこずができたすが、wjsonはraw_pairコンストラクトも提䟛したす以前の蚘事では考慮したせんでした。 これがwjrpcでどのように実装されおいるかを芋おみたしょう。


たず、wjrpcはstd :: string文字列では機胜したせんが、次のタむプを定矩したす。


 namespace wjrpc { typedef std::vector<char> data_type; typedef std::unique_ptr<data_type> data_ptr; } 

data_ptrを移動可胜なバッファヌず芋なすこずができたす。これを䜿甚するこずで、デヌタが誀っお再床コピヌされないこずを保蚌できたす。


着信するwjrpcメッセヌゞは、次の構造にデシリアラむズしようずしたす。


wjrpc ::着信
 namespace wjrpc { struct incoming { typedef data_type::iterator iterator; typedef std::pair<iterator, iterator> pair_type; pair_type method; pair_type params; pair_type result; pair_type error; pair_type id; }; } 

すべおのwjrpc ::着信芁玠は、入力バッファ内のむテレヌタのペアです。 たずえば、逆シリアル化䞭に、method.firstは入力芁求のコロンの埌にメ゜ッド名を開く匕甚笊を指し、method.secondは閉じ匕甚笊の次の䜍眮を指したす。 この構造は、芁求だけでなく、芁求ぞの応答、および゚ラヌメッセヌゞも蚘述したす。 フィヌルドに入力するこずにより、メッセヌゞのタむプを刀別するのは非垞に簡単です。 そのような構造のJSON蚘述


wjrpc :: incoming_json
 namespace wjrpc { struct incoming_json { typedef incoming::pair_type pair_type; typedef wjson::iterator_pair<pair_type> pair_json; JSON_NAME(id) JSON_NAME(method) JSON_NAME(params) JSON_NAME(result) JSON_NAME(error) typedef wjson::object< incoming, wjson::member_list< wjson::member<n_method, incoming, pair_type, &incoming::method, pair_json>, wjson::member<n_params, incoming, pair_type, &incoming::params, pair_json>, wjson::member<n_result, incoming, pair_type, &incoming::result, pair_json>, wjson::member<n_error, incoming, pair_type, &incoming::error, pair_json>, wjson::member<n_id, incoming, pair_type, &incoming::id, pair_json> > > type; typedef type::target target; typedef type::member_list member_list; typedef type::serializer serializer; }; } 

本栌的なデシリアラむれヌションはありたせん-これは、入力バッファに䜍眮を保存するこずで本質的に解析されたす。 明らかに、そのような逆シリアル化の埌、デヌタは入力バッファが存圚する間のみ有効になりたす。


wjrpc :: incoming_holderクラスは、リク゚ストバッファをキャプチャし、䞊蚘の構造に解析したす。 むンタヌフェヌスの詳现は説明したせんが、JSON-RPCの実装に䜿甚する方法を瀺したす。


最初に単玔化された1぀のメ゜ッドの䟋
 #include "calc/api/plus.hpp" #include "calc/api/plus_json.hpp" #include <wjrpc/errors/error_json.hpp> #include <wjrpc/incoming/incoming_holder.hpp> #include <wjrpc/outgoing/outgoing_holder.hpp> #include <wjrpc/outgoing/outgoing_result.hpp> #include <wjrpc/outgoing/outgoing_result_json.hpp> #include <wjrpc/outgoing/outgoing_error.hpp> #include <wjrpc/outgoing/outgoing_error_json.hpp> #include <iostream> int main() { std::vector<std::string> req_list = { "{\"method\":\"plus\", \"params\":{ \"first\":2, \"second\":3 }, \"id\" :1 }", "{\"method\":\"minus\", \"params\":{ \"first\":5, \"second\":10 }, \"id\" :1 }", "{\"method\":\"multiplies\", \"params\":{ \"first\":2, \"second\":2 }, \"id\" :1 }", "{\"method\":\"divides\", \"params\":{ \"first\":9, \"second\":3 }, \"id\" :1 }" }; std::vector<std::string> res_list; for ( auto& sreq : req_list ) { wjrpc::incoming_holder inholder( sreq ); //      inholder.parse(nullptr); //       if ( inholder.method() == "plus" ) { //     auto params = inholder.get_params<request::plus_json>(nullptr); //    wjrpc::outgoing_result<response::plus> res; res.result = std::make_unique<response::plus>(); //   res.result->value = params->first + params->second; //  id      auto raw_id = inholder.raw_id(); res.id = std::make_unique<wjrpc::data_type>( raw_id.first, raw_id.second ); //   typedef wjrpc::outgoing_result_json<response::plus_json> result_json; res_list.push_back(std::string()); result_json::serializer()( res, std::back_inserter(res_list.back()) ); } /* else if ( inholder.method() == "minus" ) { ... } */ /* else if ( inholder.method() == "multiplies" ) { ... } */ /* else if ( inholder.method() == "divides" ) { ... } */ } for ( size_t i =0; i != res_list.size(); ++i) { std::cout << req_list[i] << std::endl; std::cout << res_list[i] << std::endl; std::cout << std::endl; } } 

ここでは、゚ラヌのチェックを行っおいないため、ほずんどのコヌドが回答の圢成に占有されおいたす。 たず、incoming_holderを文字列で初期化しお解析したす。 この時点で、入力文字列は䞊蚘の着信構造に逆シリアル化されたす。 文字列に有効なjsonオブゞェクトが含たれおいる堎合、このステップぱラヌなしで合栌したす。


次に、リク゚ストのタむプを決定する必芁がありたす。 これは、「method」、「result」、「error」、および「id」フィヌルドの有無によっお簡単に実行できたす。


組み合わせメッセヌゞの皮類確認する取埗する
メ゜ッドずIDリク゚ストis_requestget_params <>
IDのないメ゜ッド気づくis_notifyget_params <>
結果ずIDリク゚ストぞの応答is_responseget_result <>
゚ラヌずIDリク゚ストぞの応答゚ラヌis_request_errorget_error <>
IDなしの゚ラヌその他の゚ラヌis_other_errorget_error <>

いずれの条件も満たされない堎合、リク゚ストが正しくないこずは明らかです。


チェックず1぀のメ゜ッドの䟋
 #include "calc/api/plus.hpp" #include "calc/api/plus_json.hpp" #include <wjrpc/errors/error_json.hpp> #include <wjrpc/incoming/incoming_holder.hpp> #include <wjrpc/outgoing/outgoing_holder.hpp> #include <wjrpc/outgoing/outgoing_result.hpp> #include <wjrpc/outgoing/outgoing_result_json.hpp> #include <wjrpc/outgoing/outgoing_error.hpp> #include <wjrpc/outgoing/outgoing_error_json.hpp> #include <iostream> int main() { std::vector<std::string> req_list = { "{\"method\":\"plus\", \"params\":{ \"first\":2, \"second\":3 }, \"id\" :1 }", "{\"method\":\"minus\", \"params\":{ \"first\":5, \"second\":10 }, \"id\" :1 }", "{\"method\":\"multiplies\", \"params\":{ \"first\":2, \"second\":2 }, \"id\" :1 }", "{\"method\":\"divides\", \"params\":{ \"first\":9, \"second\":3 }, \"id\" :1 }" }; std::vector<std::string> res_list; for ( auto& sreq : req_list ) { wjrpc::incoming_holder inholder( sreq ); wjson::json_error e; inholder.parse(&e); if ( e ) { typedef wjrpc::outgoing_error<wjrpc::error> error_type; error_type err; err.error = std::make_unique<wjrpc::parse_error>(); typedef wjrpc::outgoing_error_json<wjrpc::error_json> error_json; std::string str; error_json::serializer()(err, std::back_inserter(str)); res_list.push_back(str); } else if ( inholder.is_request() ) { auto raw_id = inholder.raw_id(); auto call_id = std::make_unique<wjrpc::data_type>( raw_id.first, raw_id.second ); //       if ( inholder.method() == "plus" ) { auto params = inholder.get_params<request::plus_json>(&e); if ( !e ) { wjrpc::outgoing_result<response::plus> res; res.result = std::make_unique<response::plus>(); res.result->value = params->first + params->second; res.id = std::move(call_id); typedef wjrpc::outgoing_result_json<response::plus_json> result_json; std::string str; result_json::serializer()( res, std::back_inserter(str) ); res_list.push_back(str); } else { typedef wjrpc::outgoing_error<wjrpc::error> error_type; error_type err; err.error = std::make_unique<wjrpc::invalid_params>(); err.id = std::move(call_id); typedef wjrpc::outgoing_error_json<wjrpc::error_json> error_json; std::string str; error_json::serializer()(err, std::back_inserter(str)); res_list.push_back(str); } } /* else if ( inholder.method() == "minus" ) { ... } */ /* else if ( inholder.method() == "multiplies" ) { ... } */ /* else if ( inholder.method() == "divides" ) { ... } */ else { typedef wjrpc::outgoing_error<wjrpc::error> error_type; error_type err; err.error = std::make_unique<wjrpc::procedure_not_found>(); err.id = std::move(call_id); typedef wjrpc::outgoing_error_json<wjrpc::error_json> error_json; std::string str; error_json::serializer()(err, std::back_inserter(str)); res_list.push_back(str); } } else { typedef wjrpc::outgoing_error<wjrpc::error> error_type; error_type err; err.error = std::make_unique<wjrpc::invalid_request>(); typedef wjrpc::outgoing_error_json<wjrpc::error_json> error_json; std::string str; error_json::serializer()(err, std::back_inserter(str)); res_list.push_back(str); } } for ( size_t i =0; i != res_list.size(); ++i) { std::cout << req_list[i] << std::endl; std::cout << res_list[i] << std::endl; std::cout << std::endl; } } 

ここで、ほずんどのコヌドぱラヌ凊理、たたは察応するメッセヌゞの圢成です。 しかし、すべおのタむプの゚ラヌに぀いおはコヌドは䌌おおり、違いぱラヌのタむプのみです。 1぀の定型関数を䜜成しお、すべおのタむプの゚ラヌをシリアル化できたす。


゚ラヌ報告
 template<typename E> void make_error(wjrpc::incoming_holder inholder, std::string& out) { typedef wjrpc::outgoing_error<wjrpc::error> common_error; common_error err; err.error = std::make_unique<E>(); if ( inholder.has_id() ) { auto id = inholder.raw_id(); err.id = std::make_unique<wjrpc::data_type>(id.first, id.second); } typedef wjrpc::outgoing_error_json<wjrpc::error_json> error_json; error_json::serializer()(err, std::back_inserter(out)); } 

゚ラヌメッセヌゞを生成するには、゚ラヌの皮類ず呌び出し識別子存圚する堎合を知るだけです。 むンホルダヌオブゞェクトは再配眮可胜であり、メッセヌゞの圢成埌は䞍芁になりたす。 この䟋では、呌び出し識別子を取埗するためにのみ䜿甚されたすが、そこから入力バッファを「ピックアップ」しお、そこにメッセヌゞをシリアル化しお、新しいバッファを䜜成しないようにするこずもできたす。


同様に、結果のシリアル化を実装できたす。 しかし、同じタむプのコヌドを取り陀き続ける前に、アプリケヌション郚分を敎理したす。アプリケヌション郚分はここでは倚少倱われ、各メ゜ッドに察しお1行だけで衚されたす。


むンタヌフェヌス


前述したように、wjrpcは、コンポヌネントのむンタヌフェむスを明瀺的に定矩する必芁があるフレヌムワヌクから匕き裂かれおいたす。 さらに、これは玔粋な仮想メ゜ッドだけの構造ではなく、メ゜ッドのパラメヌタヌに特定の制限が課せられたす。


フィヌルドが1぀しかない堎合でも、すべおの入力および出力パラメヌタヌを結合しお構造䜓にする必芁がありたす。 これは、リク゚ストのJSON蚘述を生成するのに䟿利です。事前に倉換せずに、デシリアラむズされた構造をメ゜ッドに盎接枡すこずができる堎合だけでなく、拡匵性の点からも䟿利です。


たずえば、すべおのメ゜ッドで、数倀-操䜜の結果を返したす。 可胜であれば数字で1぀のフィヌルドを持぀構造䜓で結果を説明する意味はありたすか たた、䞀般的に入力パラメヌタヌは配列䜍眮パラメヌタヌで枡すこずができたす。


ただし、芁件が少し倉曎され、付随する情報をパラメヌタヌのリストに远加する必芁があるか、たたは陀算操䜜の堎合はれロによる陀算が発生したこずを意味するフラグを远加する必芁があるずしたす。 この堎合、修正するには、むンタヌフェむスずそのすべおの実装だけでなく、䜿甚されおいるすべおの堎所も「シェむクアップ」する必芁がありたす。


JSON-RPCサヌビスがある堎合、倉曎はサヌバヌだけでなくクラむアントにも圱響したす。クラむアントのむンタヌフェヌスは䜕らかの圢で調敎する必芁がありたす。 たた、構造の堎合は、構造にフィヌルドを远加するだけです。 さらに、クラむアント郚分ずサヌバヌ郚分は個別に曎新できたす。


フレヌムワヌクのもう1぀の特城は、すべおのメ゜ッドに非同期むンタヌフェむスがあるこずです。 結果は盎接ではなく、コヌルバック関数を通じお返されたす。 たた、䞍泚意によるコピヌ゚ラヌを避けるため、入力および出力オブゞェクトはstd :: unique_ptr <>ずしお蚘述されたす。


蚈算機では、説明されおいる制限を考慮しお、次のむンタヌフェむスを取埗したす。


 struct icalc { virtual ~icalc() {} virtual void plus( request::plus::ptr req, response::plus::callback cb) = 0; virtual void minus( request::minus::ptr req, response::minus::callback cb) = 0; virtual void multiplies( request::multiplies::ptr req, response::multiplies::callback cb) = 0; virtual void divides( request::divides::ptr req, response::divides::callback cb) = 0; }; 

入力構造で定矩した補助typedefを考えるず、かなり芋栄えがしたす。 しかし、そのようなむンタヌフェヌスの実装は、比范的単玔な䟋ではかなり膚倧になる可胜性がありたす。 入力芁求がnullptrでないこずを確認し、コヌルバック関数も確認する必芁がありたす。 定矩されおいない堎合、これは通知であるこずが明らかであり、この堎合、呌び出しは無芖する必芁がありたす。 蚈算機の実装䟋に瀺すように、この機胜はテンプレヌト化が簡単です。


calc1.hpp
 #pragma once #include "icalc.hpp" class calc1 : public icalc { public: virtual void plus( request::plus::ptr req, response::plus::callback cb) override; virtual void minus( request::minus::ptr req, response::minus::callback cb) override; virtual void multiplies( request::multiplies::ptr req, response::multiplies::callback cb) override; virtual void divides( request::divides::ptr req, response::divides::callback cb) override; private: template<typename Res, typename ReqPtr, typename Callback, typename F> void impl_( ReqPtr req, Callback cb, F f); }; 

calc1.cpp
 #include "calc1.hpp" #include <wjrpc/memory.hpp> template<typename Res, typename ReqPtr, typename Callback, typename F> void calc1::impl_( ReqPtr req, Callback cb, F f) { //   if ( cb == nullptr ) return; //   if ( req == nullptr ) return cb(nullptr); auto res = std::make_unique<Res>(); res->value = f(req->first,req->second); cb( std::move(res) ); } void calc1::plus( request::plus::ptr req, response::plus::callback cb) { this->impl_<response::plus>( std::move(req), cb, [](int f, int s) { return f+s; } ); } void calc1::minus( request::minus::ptr req, response::minus::callback cb) { this->impl_<response::minus>( std::move(req), cb, [](int f, int s) { return fs; }); } void calc1::multiplies( request::multiplies::ptr req, response::multiplies::callback cb) { this->impl_<response::multiplies>( std::move(req), cb, [](int f, int s) { return f*s; }); } void calc1::divides( request::divides::ptr req, response::divides::callback cb) { this->impl_<response::divides>( std::move(req), cb, [](int f, int s) { return s!=0 ? f/s : 0; }); } 

この蚘事はただ電卓の実装に関するものではないため、䞊蚘のコヌドをベストプラクティスず芋なすべきではありたせん。 メ゜ッド呌び出しの䟋


 calc->plus( std::move(params), [](response::plus::ptr result) { 
 }); 

むンタヌフェむスのトピックに戻りたすが、サヌビスのコヌドを匕き続き「削枛」したす。 結果を非同期にシリアル化するには、着信リク゚ストを「キャプチャ」する必芁がありたす


䟋えば
 std::shared_ptr<wjrpc::incoming_holder> ph = std::make_shared<wjrpc::incoming_holder>( std::move(inholder) ); calc->plus( std::move(params), [ph, &res_list](response::plus::ptr result) { //    wjrpc::outgoing_result<response::plus> resp; resp.result = std::move(result); //    auto raw_id = ph->raw_id(); auto call_id = std::make_unique<wjrpc::data_type>( raw_id.first, raw_id.second ); resp.id = std::move(call_id); //   typedef wjrpc::outgoing_result_json<response::plus_json> result_json; typedef wjrpc::outgoing_result_json<response::plus_json> result_json; //   auto d = ph->detach(); d->clear(); result_json::serializer()( resp, std::back_inserter(d) ); res_list.push_back( std::string(d->begin(), d->end()) ); }); 

なぜなら incoming_holderは移動可胜であるため、「キャプチャ」するには、std :: shared_ptrに移動したす。 圌からバッファを取埗する方法を瀺しおいたすが、この堎合はあたり意味がありたせん-ずにかく、結果を行のリストに入れたす。 参照によるres_listのキャプチャは、単なる䟋です。 芁求は同期的に実行されるこずがわかっおいたす。


゚ラヌをシリアル化するためのテンプレヌト関数をすでに䜜成したした。回答に぀いおも同じこずを行いたす。 ただし、このためには、結果の型に加えお、その倀ずJSONの説明を枡す必芁がありたす。


ナニバヌサルク゚リレスポンスシリアラむザヌ
 template<typename ResJ> void send_response(std::shared_ptr<wjrpc::incoming_holder> ph, typename ResJ::target::ptr result, std::string& out) { typedef ResJ result_json; typedef typename result_json::target result_type; wjrpc::outgoing_result<result_type> resp; resp.result = std::move(result); auto raw_id = ph->raw_id(); resp.id = std::make_unique<wjrpc::data_type>( raw_id.first, raw_id.second ); typedef wjrpc::outgoing_result_json<result_json> response_json; typename response_json::serializer()( resp, std::back_inserter( out ) ); } 

ここでは、テンプレヌトパラメヌタを明瀺的に指定する必芁がありたす。これは、応答構造のJSON蚘述であり、蚘述された構造のタむプを取埗できたす。 この関数を䜿甚するず、各JSON-RPCメ゜ッドのコヌドが倧幅に簡玠化されたす。


plusメ゜ッドの新しいバヌゞョン
 if ( inholder.method() == "plus" ) { //   auto params = inholder.get_params<request::plus_json>(&e); if ( !e ) { std::shared_ptr<wjrpc::incoming_holder> ph = std::make_shared<wjrpc::incoming_holder>( std::move(inholder) ); calc->plus( std::move(params), std::bind( send_response<response::plus_json>, ph, std::placeholders::_1, std::ref(out)) ); } else { make_error<wjrpc::invalid_params>(std::move(inholder), out ); } } // else if ( inholder.method() == "minus" ) { ... } // else if ( inholder.method() == "multiplies" ) { .... } // else if ( inholder.method() == "divides" ) { .... } else { make_error<wjrpc::procedure_not_found>(std::move(inholder), out ); } 

各メ゜ッドに぀いお、コヌドは最小限に削枛されたしたが、これでは十分ではありたせん。 パラメヌタの取埗ず゚ラヌのチェックも同じタむプのコヌドです。


シリアル化ずメ゜ッド呌び出し
 template< typename JParams, typename JResult, void (icalc::*mem_ptr)( std::unique_ptr<typename JParams::target>, std::function< void(std::unique_ptr<typename JResult::target>) > ) > void invoke(wjrpc::incoming_holder inholder, std::shared_ptr<icalc> calc, std::string& out) { typedef JParams params_json; typedef JResult result_json; wjson::json_error e; auto params = inholder.get_params<params_json>(&e); if ( !e ) { std::shared_ptr<wjrpc::incoming_holder> ph = std::make_shared<wjrpc::incoming_holder>( std::move(inholder) ); (calc.get()->*mem_ptr)( std::move(params), std::bind( send_response<result_json>, ph, std::placeholders::_1, std::ref(out) ) ); } else { out = make_error<wjrpc::invalid_params>(); } } 

これはwjrpcで実装されおいるものずほが同じです。 その結果、デモ䟋のコヌドは最小限に削枛されたすここで、すべおのメ゜ッドの実装を既に提䟛できたす。


「手動凊理」を䜿甚した䟋の最終バヌゞョン
 int main() { std::vector<std::string> req_list = { "{\"method\":\"plus\", \"params\":{ \"first\":2, \"second\":3 }, \"id\" :1 }", "{\"method\":\"minus\", \"params\":{ \"first\":5, \"second\":10 }, \"id\" :1 }", "{\"method\":\"multiplies\", \"params\":{ \"first\":2, \"second\":2 }, \"id\" :1 }", "{\"method\":\"divides\", \"params\":{ \"first\":9, \"second\":3 }, \"id\" :1 }" }; std::vector<std::string> res_list; auto calc = std::make_shared<calc1>(); for ( auto& sreq : req_list ) { res_list.push_back( std::string() ); std::string& out = res_list.back(); wjrpc::incoming_holder inholder( sreq ); wjson::json_error e; inholder.parse(&e); if ( e ) { out = make_error<wjrpc::parse_error>(); } else if ( inholder.is_request() ) { //       if ( inholder.method() == "plus" ) { invoke<request::plus_json, response::plus_json, &icalc::plus>( std::move(inholder), calc, out ); } else if ( inholder.method() == "minus" ) { invoke<request::minus_json, response::minus_json, &icalc::minus>( std::move(inholder), calc, out ); } else if ( inholder.method() == "multiplies" ) { invoke<request::multiplies_json, response::multiplies_json, &icalc::multiplies>( std::move(inholder), calc, out ); } else if ( inholder.method() == "divides" ) { invoke<request::divides_json, response::divides_json, &icalc::divides>( std::move(inholder), calc, out ); } else { out = make_error<wjrpc::procedure_not_found>(); } } else { out = make_error<wjrpc::invalid_request>(); } } for ( size_t i =0; i != res_list.size(); ++i) { std::cout << req_list[i] << std::endl; std::cout << res_list[i] << std::endl; std::cout << std::endl; } } 

実行時のコヌド量を枛らしたいずいうSuchな欲求は、いく぀かの理由によるものです。 かなり耇雑なデザむンを䜿甚しお䞍芁なifを削陀するこずで、コヌドの量を枛らすだけでなく、プログラマヌがnagovoditに満足する可胜性のある堎所も削陀したす。 たた、コピヌペヌストなどのプログラマヌ、特にシリアル化に関連する面癜くないコヌド、プロゞェクト党䜓ぞの拡散、たたは他のプロゞェクトぞの持ち蟌みさえも可胜です。 怠azineは進歩の゚ンゞンであり、人々が仕事を枛らすこずを可胜にする䜕かを発明させるずきです。 しかし、物事が埌たで延期される堎合はそうではありたせん。 些现なチェックのように思えたすが、蚀葉で蚀えば、これはただプロトタむプであり、数行のコヌドを曞くのは延期され、忘れられお同時にプロゞェクト党䜓にコピヌされ、他のプログラマヌによっお取り䞊げられたす。


実際、wjrpcの怜蚎はただ始たっおいたせん。wjsonを䜿甚しおリク゚ストがどのように蚘述されるかを瀺し、テストケヌスのむンタヌフェむスずアプリケヌションロゞックを蚘述し、JSON-RPCを手動で実装する方法を怜蚎したした。完党なwjrpcの䟋を次に瀺したす。


 #include "calc/calc1.hpp" #include "calc/api/plus_json.hpp" #include "calc/api/minus_json.hpp" #include "calc/api/multiplies_json.hpp" #include "calc/api/divides_json.hpp" #include <wjrpc/handler.hpp> #include <wjrpc/method.hpp> #include <iostream> #include <functional> JSONRPC_TAG(plus) JSONRPC_TAG(minus) JSONRPC_TAG(multiplies) JSONRPC_TAG(divides) struct method_list: wjrpc::method_list < wjrpc::target<icalc>, wjrpc::invoke_method<_plus_, request::plus_json, response::plus_json, icalc, &icalc::plus>, wjrpc::invoke_method<_minus_, request::minus_json, response::minus_json, icalc, &icalc::minus>, wjrpc::invoke_method<_multiplies_, request::multiplies_json, response::multiplies_json, icalc, &icalc::multiplies>, wjrpc::invoke_method<_divides_, request::divides_json, response::divides_json, icalc, &icalc::divides> >{}; class handler: public wjrpc::handler<method_list> {}; int main() { std::vector<std::string> req_list = { "{\"method\":\"plus\", \"params\":{ \"first\":2, \"second\":3 }, \"id\" :1 }", "{\"method\":\"minus\", \"params\":{ \"first\":5, \"second\":10 }, \"id\" :1 }", "{\"method\":\"multiplies\", \"params\":{ \"first\":2, \"second\":2 }, \"id\" :1 }", "{\"method\":\"divides\", \"params\":{ \"first\":9, \"second\":3 }, \"id\" :1 }" }; std::vector<std::string> res_list; auto calc = std::make_shared<calc1>(); handler h; handler::options_type opt; opt.target = calc; h.start(opt, 1); for ( auto& sreq : req_list ) { h.perform( sreq, [&res_list](std::string out) { res_list.push_back(out);} ); } for ( size_t i =0; i != res_list.size(); ++i) { std::cout << req_list[i] << std::endl; std::cout << res_list[i] << std::endl; std::cout << std::endl; } } 

JSONRPC_TAGを䜿甚しお、wjsonのJSON NAMEず同様に、テンプレヌトパラメヌタヌずしお枡すメ゜ッド名を指定したす。唯䞀の違いは、プレフィックスnの代わりにアンダヌスコアで囲たれた゚ンティティの名前です。


次に、wjrpc :: method_listおよびwjrpc :: invoke_methodを䜿甚しお、䜿甚可胜なすべおのメ゜ッドに぀いお説明したす。メ゜ッドのリストはwjrpc ::ハンドラヌhandlerに枡されたす。リストは、メ゜ッドずずもに、ハンドラヌが動䜜するオブゞェクトのむンタヌフェむスタむプを蚘述したした。


perform_io, wjrpc::data_ptr.


 typedef std::vector<char> data_type; typedef std::unique_ptr<data_type> data_ptr; typedef std::function< void(data_ptr) > output_handler_t; void perform_io(data_ptr d, output_handler_t handler) { 
 } void perform(std::string str, std::function<void(std::string)> handler) { auto d = std::make_unique<data_type>( str.begin(), str.end() ); this->perform_io( std::move(d), [handler](data_ptr d) { handler( std::string(d->begin(), d->end()) ); }); } 

, . , , ,


,
 struct plus_handler { template<typename T> void operator()(T& t, request::plus::ptr req) { //   t.target()->plus( std::move(req), nullptr ); } template<typename T, typename Handler> void operator()(T& t, request::plus::ptr req, Handler handler) { //   t.target()->plus( std::move(req), [handler](response::plus::ptr res) { if ( res != nullptr ) handler( std::move(res), nullptr ); else handler( nullptr, std::make_unique<wjrpc::service_unavailable>() ); }); } }; 

 struct plus_handler { template<typename T> void operator()(T&, request::plus::ptr req) { } template<typename T, typename Handler> void operator()(T&, request::plus::ptr req, Handler handler) { if (req==nullptr) { handler( nullptr, std::make_unique<wjrpc::invalid_params>() ); return; } auto res = std::make_unique<response::plus>(); res->value = req->first + req->second; handler( std::move(res), nullptr ); } }; 

handler , , . t — JSON-RPC- ( self python). :


 struct method_list: wjrpc::method_list < wjrpc::target<icalc>, wjrpc::method< wjrpc::name<_plus_>, wjrpc::invoke<request::plus_json, response::plus_json, plus_handler> >, wjrpc::invoke_method<_minus_, request::minus_json, response::minus_json, icalc, &icalc::minus>, wjrpc::invoke_method<_multiplies_, request::multiplies_json, response::multiplies_json, icalc, &icalc::multiplies>, wjrpc::invoke_method<_divides_, request::divides_json, response::divides_json, icalc, &icalc::divides> >{}; 

, invoke_method<> wjrpc::method<>, :


mem_fun_handler
 template< typename Params, typename Result, typename I, void (I::*mem_ptr)( std::unique_ptr<Params>, std::function< void(std::unique_ptr<Result>) > ) > struct mem_fun_handler { typedef std::unique_ptr<Params> request_ptr; typedef std::unique_ptr<Result> responce_ptr; typedef std::unique_ptr< error> json_error_ptr; typedef std::function< void(responce_ptr, json_error_ptr) > jsonrpc_callback; template<typename T> void operator()(T& t, request_ptr req) const; template<typename T> void operator()(T& t, request_ptr req, jsonrpc_callback cb) const; }; 

, JSON-RPC , , .


, ( ), , ( ) . . , - , . . , ? - . , , . , , . , , , .


- “” . wjrpc::invoke_method. , JSON-RPC. “” , .


, wjrpc::invoke_method.


 struct icalc { virtual ~icalc() {} virtual request::plus::ptr plus( request::plus::ptr req) = 0; virtual request::minus::ptr minus( request::minus::ptr req) = 0; virtual request::multiplies::ptr multiplies( request::multiplies::ptr req) = 0; virtual request::divides::ptr divides( request::divides::ptr req) = 0; }; 

wjrpc::handler<> . callback, , , , , .


 calc->plus( std::move(req), [this](response::plus::ptr) {} ); 

 std::shared_ptr<calc> pthis = this->shared_from_this(); calc->plus( std::move(req), [pthis](response::plus::ptr) {} ); 

, .. .


 std::weak_ptr<calc> wthis = this->shared_from_this(); calc->plus( std::move(req), [wthis](response::plus::ptr) { if ( auto pthis = wthis.lock() ) { /* 
 */ } } ); 

, (, ). , , callback- .


 std::weak_ptr<int> w = this->_p; /* _p = std::shared_ptr<int>(1);*/ std::weak_ptr<calc> wthis = this->shared_from_this(); calc->plus( std::move(req), [wthis, w](response::plus::ptr) { if ( auto pthis = wthis.lock() ) { if ( nullptr == w.lock() ) return; /* 
 */ } } ); 

( wjrpc)


callback-
 template<typename H> class owner_handler { public: typedef std::weak_ptr<int> weak_type; owner_handler() = default; owner_handler(H&& h, weak_type alive) : _handler( std::forward<H>(h) ) , _alive(alive) { } template <class... Args> auto operator()(Args&&... args) -> typename std::result_of< H(Args&&...) >::type { if ( auto p = _alive.lock() ) { return _handler( std::forward<Args>(args)... ); } return typename std::result_of< H(Args&&...) >::type(); } private: H _handler; weak_type _alive; }; 

( )
 class owner { public: typedef std::shared_ptr<int> alive_type; typedef std::weak_ptr<int> weak_type; owner() : _alive( std::make_shared<int>(1) ) { } owner(const owner& ) = delete; owner& operator = (const owner& ) = delete; owner(owner&& ) = default; owner& operator = (owner&& ) = default; alive_type& alive() { return _alive; } const alive_type& alive() const { return _alive; } void reset() { _alive = std::make_shared<int>(*_alive + 1); } template<typename Handler> owner_handler<typename std::remove_reference<Handler>::type> wrap(Handler&& h) const { return owner_handler< typename std::remove_reference<Handler>::type >( std::forward<Handler>(h), std::weak_ptr<int>(_alive) ); } private: mutable alive_type _alive; }; 

䜿甚䟋
 // owner -   std::weak_ptr<calc> wthis = this->shared_from_this(); calc->plus( std::move(req), this->wrap([wthis](response::plus::ptr) { if ( auto pthis = wthis.lock() ) { /* 
 */ } })); // 
 //    owner::reset(); //  callback-    

. . , . , , . , .


JSON-RPC Engine


wjrpc::engine – , , jsonrpc-, . , , , wjrpc::engine. . callback-, , . wjrpc::engine, , , , .


, icalc. , plus, .


calc/calc_p.hpp
 #pragma once #include "icalc.hpp" class calc_p : public icalc { public: void initialize(std::shared_ptr<icalc>); virtual void plus( request::plus::ptr req, response::plus::callback cb) override; virtual void minus( request::minus::ptr req, response::minus::callback cb) override; virtual void multiplies( request::multiplies::ptr req, response::multiplies::callback cb) override; virtual void divides( request::divides::ptr req, response::divides::callback cb) override; private: template<typename ReqPtr, typename Callback> bool check_( ReqPtr& req, Callback& cb); std::shared_ptr<icalc> _next; }; 

calc/calc_p.cpp
 #include "calc_p.hpp" #include <memory> void calc_p::initialize(std::shared_ptr<icalc> next) { _next = next; } void calc_p::plus( request::plus::ptr req, response::plus::callback cb) { if ( !this->check_(req, cb)) return; req->first++; req->second++; _next->plus(std::move(req), [cb](response::plus::ptr res) { res->value++; cb(std::move(res) ); }); } void calc_p::minus( request::minus::ptr req, response::minus::callback cb) { if ( this->check_(req, cb)) _next->minus(std::move(req), std::move(cb) ); } void calc_p::multiplies( request::multiplies::ptr req, response::multiplies::callback cb) { if ( this->check_(req, cb)) _next->multiplies(std::move(req), std::move(cb) ); } void calc_p::divides( request::divides::ptr req, response::divides::callback cb) { if ( this->check_(req, cb)) _next->divides(std::move(req), std::move(cb) ); } template<typename ReqPtr, typename Callback> bool calc_p::check_( ReqPtr& req, Callback& cb) { if ( cb==nullptr ) return false; if ( req != nullptr ) return true; cb(nullptr); return false; } 

, icalc, , plus, “”. .


, : , , . , , .


, , , JSON-RPC .


 JSONRPC_TAG(plus) JSONRPC_TAG(minus) JSONRPC_TAG(multiplies) JSONRPC_TAG(divides) struct method_list: wjrpc::method_list < wjrpc::call_method<_plus_, request::plus_json, response::plus_json>, wjrpc::call_method<_minus_, request::minus_json, response::minus_json>, wjrpc::call_method<_multiplies_, request::multiplies_json, response::multiplies_json>, wjrpc::call_method<_divides_, request::divides_json, response::divides_json, icalc> > {}; 

, , , JSON- . — icalc:


 class handler : public ::wjrpc::handler<method_list> , public icalc { public: virtual void plus( request::plus::ptr req, response::plus::callback cb) override { this->template call<_plus_>( std::move(req), cb, nullptr ); } virtual void minus( request::minus::ptr req, response::minus::callback cb) override { this->template call<_minus_>( std::move(req), cb, nullptr ); } virtual void multiplies( request::multiplies::ptr req, response::multiplies::callback cb) override { this->template call<_multiplies_>( std::move(req), cb, nullptr ); } virtual void divides( request::divides::ptr req, response::divides::callback cb) override { this->template call<_divides_>( std::move(req), cb, nullptr ); } }; 

— call<> , , , . , . , callback nullptr.


JSON-RPC , callback nullptr, , , . JSON-RPC . , .


, , :




, , , , . , , , . , . — , . , , , .


. callback, callback, , . JSON-RPC , id JSON-RPC . callback , callback, .


calc/calc_p.cpp
 #include "calc/calc1.hpp" #include "calc/calc_p.hpp" #include "calc/api/plus_json.hpp" #include "calc/api/minus_json.hpp" #include "calc/api/multiplies_json.hpp" #include "calc/api/divides_json.hpp" #include <wjrpc/engine.hpp> #include <wjrpc/handler.hpp> #include <wjrpc/method.hpp> #include <iostream> #include <functional> namespace service { JSONRPC_TAG(plus) JSONRPC_TAG(minus) JSONRPC_TAG(multiplies) JSONRPC_TAG(divides) struct method_list: wjrpc::method_list < wjrpc::target<icalc>, wjrpc::invoke_method<_plus_, request::plus_json, response::plus_json, icalc, &icalc::plus>, wjrpc::invoke_method<_minus_, request::minus_json, response::minus_json, icalc, &icalc::minus>, wjrpc::invoke_method<_multiplies_, request::multiplies_json, response::multiplies_json, icalc, &icalc::multiplies>, wjrpc::invoke_method<_divides_, request::divides_json, response::divides_json, icalc, &icalc::divides> >{}; class handler: public ::wjrpc::handler<method_list> {}; typedef wjrpc::engine<handler> engine_type; } namespace gateway { JSONRPC_TAG(plus) JSONRPC_TAG(minus) JSONRPC_TAG(multiplies) JSONRPC_TAG(divides) struct method_list: wjrpc::method_list < wjrpc::call_method<_plus_, request::plus_json, response::plus_json>, wjrpc::call_method<_minus_, request::minus_json, response::minus_json>, wjrpc::call_method<_multiplies_, request::multiplies_json, response::multiplies_json>, wjrpc::call_method<_divides_, request::divides_json, response::divides_json> > {}; class handler : public ::wjrpc::handler<method_list> , public icalc { public: virtual void plus( request::plus::ptr req, response::plus::callback cb) override { this->template call<_plus_>( std::move(req), cb, nullptr ); } virtual void minus( request::minus::ptr req, response::minus::callback cb) override { this->template call<_minus_>( std::move(req), cb, nullptr ); } virtual void multiplies( request::multiplies::ptr req, response::multiplies::callback cb) override { this->template call<_multiplies_>( std::move(req), cb, nullptr ); } virtual void divides( request::divides::ptr req, response::divides::callback cb) override { this->template call<_divides_>( std::move(req), cb, nullptr ); } }; typedef wjrpc::engine<handler> engine_type; } int main() { //  N1 auto prx1 = std::make_shared<calc_p>(); //  auto gtw = std::make_shared<gateway::engine_type>(); //  auto srv = std::make_shared<service::engine_type>(); //  N2 auto prx2 = std::make_shared<calc_p>(); //  auto clc = std::make_shared<calc1>(); //      prx2->initialize(clc); //         service::engine_type::options_type srv_opt; srv_opt.target = prx2; srv->start(srv_opt, 11); //   gateway::engine_type::options_type cli_opt; gtw->start(cli_opt, 22); //         gtw->reg_io(33, [srv]( wjrpc::data_ptr d, wjrpc::io_id_t /*io_id*/, wjrpc::output_handler_t handler) { std::cout << " REQUEST: " << std::string( d->begin(), d->end() ) << std::endl; srv->perform_io(std::move(d), 44, [handler](wjrpc::data_ptr d) { //     JSON-RPC  std::cout << " RESPONSE: " << std::string( d->begin(), d->end() ) << std::endl; handler(std::move(d) ); }); }); //       ID auto gtwh = gtw->find(33); //       prx1->initialize(gtwh); //  plus   (prx1->gtw->srv->prx2->clc) auto plus = std::make_unique<request::plus>(); plus->first = 1; plus->second = 2; prx1->plus( std::move(plus), [](response::plus::ptr res) { std::cout << "1+2=" << res->value << std::endl;; }); //  plus   (gtw->srv->prx2->clc) auto minus = std::make_unique<request::minus>(); minus->first = 4; minus->second = 3; gtwh->minus( std::move(minus), [](response::minus::ptr res) { std::cout << "4-3=" << res->value << std::endl;; }); } 

結果


 REQUEST: {"jsonrpc":"2.0","method":"plus","params":{"first":2,"second":3},"id":1} RESPONSE: {"jsonrpc":"2.0","result":{"value":8},"id":1} 1+2=9 REQUEST: {"jsonrpc":"2.0","method":"minus","params":{"first":4,"second":3},"id":2} RESPONSE: {"jsonrpc":"2.0","result":{"value":1},"id":2} 4-3=1 

, JSON-RPC 2 3 1 2. , , 8. , 9. , , , munus , .


— , , , , , , :


create_id
 inline wjrpc::io_id_t create_id() { static std::atomic<wjrpc::io_id_t> counter( (wjrpc::io_id_t(1)) ); return counter.fetch_add(1); } 

. , :


  gtw->reg_io(33, []( wjrpc::data_ptr, wjrpc::io_id_t, wjrpc::output_handler_t) 

JSON-RPC , . - , , , . . . 44 — , . service::engine_type, ( , wjrpc ), .


(::pipe), , , , , - , , , . examples , , , .


wjson, , wjrpc::incoming_holder JSON-RPC . , wjrpc::handler , run-time .


wjrpc . faslib wjson . :


 git clone https://github.com/migashko/faslib.git git clone https://github.com/mambaru/wjson.git git clone https://github.com/mambaru/wjrpc.git #      wjrpc cd faslib mkdir build cd build cmake .. #     cd ../../wjrpc mkdir build cd build cmake -DWJRPC_BUILD_ALL=ON .. make 


Source: https://habr.com/ru/post/J312994/


All Articles