概要
picojson を使い JSON-RPC 2.0 を扱う C++ のライブラリー実装をやっつけたので紹介したい。
特徴
- 依存ライブラリー
std=c++1z
- 用途と機能
-
JSON-RPC 2.0 を扱う
- request を picojson::value で受けて response ( result or error )を picojson::value で返すファンクター群を
boost::signals2::signal
でstd::string
のメソッド名とのunordered_map
でマッピング・管理する picojson ベースの JSON-RPC 2.0 サーバーserver_type
( HTTP 機能は無し ) - request をお手軽に生成する
make_request
やmake_request_with_uuidv4
- request から id, method, params を取得しつつエラーがあれば例外を投げたり、例外から error の response を返す機能
- result の response をお手軽に生成する
make_response
やmeke_response_from_request
- etc.
- request を picojson::value で受けて response ( result or error )を picojson::value で返すファンクター群を
-
JSON-RPC 2.0 を扱う
実装
使い方
基本的な使い方は次の様な手順を想定しています:
-
server_type
のオブジェクトs
を生成 -
s
に.connect
でserver_type::method_functor_type
な JSON-RPC のメソッドを接続- 必要に応じて第3引数に
shared_ptr
オブジェクトを投げてメソッドの接続の自動管理(track_foreign
)を行う(多くの場合にコネクターの手動管理よりもお勧め)
- 必要に応じて第3引数に
- HTTP などで受け取った JSON 文字列の JSON-RPC 2.0 のリクエストを picojson::value にして
s
のoperator()
へ放り投げる -
s
に放り投げて返って来た picojson::value ( result or error )を HTTP などで返す
example
#include <usagi/json/picojson/rpc/jsonrpc20.hxx>
#include <memory>
auto main() -> int
{
using namespace usagi::json::picojson;
using namespace usagi::json::picojson::rpc::jsonrpc20;
server_type s;
// 野生のλ
auto fa = []( const value_type& request ){ return value_type( request.get< double >() * 2.0 ); };
// s に存在しないメソッド aaa を uuidv4 の id 付きで呼ぶ
std::cout << "trial-1: " << s( make_request_with_uuidv4( "aaa", value_type( 1.23 ) ) ) << '\n';
// s に fa をメソッド名 aaa として接続しコネクター ca を取得
auto ca = s.connect( "aaa", fa );
// s に存在するメソッド aaa を呼ぶ(期待される型の引数を伴う)
std::cout << "trial-2: " << s( make_request_with_uuidv4( "aaa", value_type( 1.23 ) ) ) << '\n';
// s に存在するメソッド aaa を呼ぶ(期待されない型の引数を伴う)
std::cout << "trial-3: " << s( make_request_with_uuidv4( "aaa", value_type( "1.23" ) ) ) << '\n';
// s に接続した fa をコネクター ca から切断
ca.disconnect();
// 有効なスロットが存在しないメソッド aaa を呼ぶ
std::cout << "trial-4: " << s( make_request_with_uuidv4( "aaa", value_type( 1.23 ) ) ) << '\n';
// shared_ptr の functor
auto fb = std::make_shared< server_type::method_functor_type >( []( const auto& ) { return value_type(); } );
// s に存在しないメソッド bbb を id なし( = null )で呼ぶ
std::cout << "trial-5: " << s( make_request( "bbb", value_type( 1.23 ) ) ) << '\n';
// s に fb の中身の functor を fb をトラッキングさせつつ接続しコネクター cb を取得
auto cb = s.connect( "bbb", *fb, fb );
// s に存在するメソッド bbb を呼ぶ
std::cout << "trial-6: " << s( make_request( "bbb" ) ) << '\n';
// s のメソッド bbb に接続した fb の中身の functor との接続をトラッキングする fb を無効化する(同時に接続が自動的に切断される)
fb.reset();
// (自動的に切断され)有効なスロットが存在しないメソッド bbb を呼ぶ
std::cout << "trial-7: " << s( make_request( "bbb" ) ) << '\n';
}
example の動作結果
trial-1: {"error":{"code":-32601,"data":null,"message":"Method not found"},"id":"7b474b5e-6df1-4200-8ba0-74508f8e6206","jsonrpc":"2.0"}
trial-2: {"id":"869650e4-0869-4304-ace6-27862ff5817b","jsonrpc":"2.0","result":2.46}
trial-3: {"error":{"code":-32603,"data":null,"message":"Internal error ( \"type mismatch! call is<type>() before get<type>()\" && is<double>() ) "},"id":"2d1e3204-493a-4edc-88b2-73b341546e1c","jsonrpc":"2.0"}
trial-4: {"error":{"code":-32601,"data":null,"message":"Method not found ( sigal is empty. ) "},"id":"4c102ebc-23a7-466a-9f4c-b9fcc1c727a5","jsonrpc":"2.0"}
trial-5: {"error":{"code":-32601,"data":null,"message":"Method not found"},"id":null,"jsonrpc":"2.0"}
trial-6: {"id":null,"jsonrpc":"2.0","result":null}
trial-7: {"error":{"code":-32601,"data":null,"message":"Method not found ( sigal is empty. ) "},"id":null,"jsonrpc":"2.0"}
cmake 経由で example を試す場合
git clone git@github.com:usagi/usagi.git
cd usagi
mkdir build
cd build
cmake .. -G Ninja -DEXAMPLES=1
ninja jsonrpc20
./jsonrpc20