picojson で JSON-RPC-2.0 を扱う C++ のライブラリー実装

# 概要

[picojson]() を使い [JSON-RPC 2.0](http://www.jsonrpc.org/specification) を扱う C++ のライブラリー実装をやっつけたので紹介したい。

## 特徴

- 依存ライブラリー
    - [picojson](https://github.com/kazuho/picojson/)
    - [boost::signals2](http://www.boost.org/doc/libs/1_63_0/doc/html/signals2.html)
    - [boost::uuids](http://www.boost.org/doc/libs/1_63_0/libs/uuid/uuid.html)
- `std=c++1z`
- 用途と機能
    - [JSON-RPC 2.0](http://www.jsonrpc.org/specification) を扱う
        - 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.

# 実装

- https://github.com/usagi/usagi/tree/master/include/usagi/json/picojson/rpc/jsonrpc20

# 使い方

基本的な使い方は次の様な手順を想定しています: 

1. `server_type` のオブジェクト `s` を生成
2. `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()` へ放り投げる
4. `s` に放り投げて返って来た picojson::value （ result or error ）を HTTP などで返す

## example

- https://github.com/usagi/usagi/blob/master/example/jsonrpc20.cxx

```C++
#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 の動作結果

```json
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 を試す場合

```zsh
git clone git@github.com:usagi/usagi.git
cd usagi
mkdir build
cd build
cmake .. -G Ninja -DEXAMPLES=1
ninja jsonrpc20
./jsonrpc20
```