2022/03/20


おなじみC/C++から使えるJSONライブラリを紹介するコーナー。まずは過去のまとめ。

C/C++ から扱える JSON ライブラリを数多く紹介してきましたが、パフォーマンスの観点では simdjson が不動の物にしていました。

GitHub - simdjson/simdjson: Parsing gigabytes of JSON per second

Documentation Usage documentation is available: Basics is an overview of how to use simdjson and its...

https://github.com/simdjson/simdjson

がしかし yyjson の登場により、この認識を改めなければならなくなりました。

GitHub - ibireme/yyjson: The fastest JSON library in C

yyjson A high performance JSON library written in ANSI C. Features Fast : can read or write gigabyte...

https://github.com/ibireme/yyjson

まずはこのベンチマーク結果を見て下さい。

benchmark

追記 このグラフは simdjson の DOM API という古い API が使われていた頃の物らしく、ondemand API という最近の API を使うとまた結果が異なる様です。

一部で simdjson の方が勝ってはいるものの、殆どのケースで yyjson が上回っています。

yyjson の特徴は以下の通り。

  • 高速: 最新の CPU で秒間ギガバイトの JSON データを読み書きできる
  • ポータブル: ANSI C(C89)に準拠
  • 標準: RFC8259 標準に厳密に準拠
  • 安全: 完全な JSON 形式、数値形式、UTF-8 バリデーション
  • 精度: int64、uint64、および double の数値を正確に読み書き可能
  • 制限が少ない: 無制限の JSON レベル、\u0000 および null で終了しない文字列をサポート
  • 拡張可能: コメント、末尾のコンマ、nan/inf、カスタムメモリアロケータを許可するオプション
  • 開発者向け: 1個のヘッダファイル1個のCファイルのみ、統合が簡単

ソースの中を見てみましたが、随所で SIMD による最適化が行われています。データ構造も SIMD で扱う為に 64 ビット単位(タイプ、長さ、ペイロードの3つで 128 ビット)にしてあります。

いつもの通りサンプルコードを書いてみました。本ブログサイトのエントリを JSON 形式にした物をパースしてみます。

#include 
#include 

int
main() {
  yyjson_read_err err;
  yyjson_doc *doc = yyjson_read_file("foo.json",
      YYJSON_READ_NOFLAG,
      NULL,
      &err);
  if (!doc) {
    fprintf(stderr"%s\n", err.msg);
    exit(1);
  }

  yyjson_val *root = yyjson_doc_get_root(doc);

  yyjson_val *title = yyjson_obj_get(root, "title");
  printf("title: %s\n", yyjson_get_str(title));

  yyjson_val *entries = yyjson_obj_get(root, "entries");

  size_t idx, max;
  yyjson_val *entry;
  yyjson_arr_foreach(entries, idx, max, entry) {
    title = yyjson_obj_get(entry, "title");
    printf("%s\n", yyjson_get_str(title));
  }

  yyjson_doc_free(doc); 
  return 0;
}

直感的で parson や jannson を使った事がある方であればどんな API かすぐに分かると思います。

ライセンスも MIT ですので業務で使う事もできますね。

Posted at by



2021/03/03


おなじみC/C++から使えるJSONライブラリを紹介するコーナー。まずは過去のまとめ。

これまで C++ から扱える JSON パーサを数多く紹介してきましたが、いずれもコメントを扱えるものはありませんでした。今回紹介する jsoncpp はコメントも扱えます。

GitHub - open-source-parsers/jsoncpp: A C++ library for interacting with JSON.

JsonCpp JSON is a lightweight data-interchange format. It can represent numbers, strings, ordered se...

https://github.com/open-source-parsers/jsoncpp

特徴は以下の通り。

  • コメントが扱える
  • Amalgamated code を生成できる
  • 古いコンパイラをサポートしている
  • Public Domain

触ってみた感じは json.hpp に近いです。

#include "json/json.h"
#include 

int
main(int argc, char* argv[]) {
  const std::string rawJson = R"(
  {
    "Age": 20,
    "Name": "mattn" // your name
  }
  )";
  Json::Value root;
  Json::CharReaderBuilder builder;
  builder["collectComments"] = true;
  JSONCPP_STRING errs;
  auto reader = builder.newCharReader();
  if (!reader->parse(rawJson.c_str(), rawJson.cend().base(), &root, &errs)) {
    std::cout << errs << std::endl;
    return EXIT_FAILURE;
  }
  std::cout << root["Age"].asInt64() << std::endl;
  std::cout << root["Name"].asString() << std::endl;
  std::cout << root["Name"].getComment(Json::CommentPlacement::commentAfterOnSameLine) << std::endl;
  return EXIT_SUCCESS;
}

collectComments を true にする事でパース時にコメントを収集してくれます。getComment に指定する commentBefore, commentAfterOnSameLine, commentAfter によりノードの前方、同一行または後方、後方、のいずれかのコメントを得られます。

jsoncpp にはこれだけでなく、とても便利な色々なオプションが用意されています。

collectComments: false or true

true でコメントを収集し、シリアライズ時にコメントを戻す

allowComments: false or true

コメントを許可

allowTrailingCommas: false or true

ケツカンマを許可

strictRoot: false or true

ルートノートに配列またはオブジェクトを必須とする

allowDroppedNullPlaceholders: false or true

"null" という文字列をシリアライズ時にドロップする。

allowNumericKeys: false or true

オブジェクトに数値のキーを許可

allowSingleQuotes: false or true

シングルクオートを許可

stackLimit: 数値

階層の限界を指定。オーバーすると例外が発生。

failIfExtra: false or true

余計な空白をエラーとして扱う。

rejectDupKeys: false or true

オブジェクトのキーに重複を許可。

allowSpecialFloats: false or true

NaN や inf の様な特殊な浮動小数点を許可。


これだけあれば多少ユルユル仕様な設定ファイル向けの JSON ファイルも読み込めると思います。

また jsoncpp は通常、ライブラリ形式(.a)が提供されますが、付属の amalgamate.py を使う事で、ヘッダファイル1つとソースファイル1つに纏められた形式にする事もできる為、以下の様に簡単にソースコードをビルドする事ができます。

$ ls -R
.:
json  jsoncpp.cpp  main.cpp

./json:
json.h  json-forwards.h

$ gcc -I. -o app main.cpp jsoncpp.cpp

jsoncpp は基本、パブリックドメインのライセンスで提供されています。しかし地域によってはパブリックドメインは実在しない扱いとなります。jsoncpp ではその様な地域の場合は MIT ライセンスのもと配布されるという事になっています。

ここまで融通の効く C++ 向け JSON パーサは僕が知る限り他には無いと思います。

Posted at by



2020/06/17


今まで C++ でちょっとしたウェブアプリを作る際は crow という micro-framework を使ってきました。

GitHub - ipkn/crow: Crow is very fast and easy to use C++ micro web framework (inspired by Python Flask)

How to Build If you just want to use crow, copy amalgamate/crow_all.h and include it. Requirements C...

https://github.com/ipkn/crow

ヘッダオンリーで使えてとても便利だったのですが、boost に依存している点があまり好きじゃなかったのと、最近 crow の開発が止まり最新の boost でビルド出来なくなってしまったので、自分で作る事にしました。

GitHub - mattn/clask: Web micro-framework like flask in C++.

# include " clask/core.hpp " int main () { auto s = clask::server (); s. GET ( " / " , [](clask::req...

https://github.com/mattn/clask

C++ から flask ぽく使える事を目指しました。ヘッダオンリーで使えます。HTTP ヘッダを解析する所だけ kazuho さんの picohttpparser を使わせて頂いています。

#include "clask/core.hpp"

int main() {
  auto s = clask::server();
  s.GET("/", [](clask::request& req) {
    return "OK!";
  });
  s.GET("/foo", [](clask::response& resp, clask::request& req) {
    resp.set_header("content-type""text/html");
    resp.write("he<b>l</b>lo");
  });
  s.run();
}

boost にも依存していませんし Windows でも問題なく動作します。ただしコンパイルには C++17 をサポートしているコンパイラが必要です。keep-alive に対応したマルチスレッドなウェブサーバなので、静的な2文字("OK)くらいのコンテンツだと 70000req/sec くらい出ます(ログ出力を無効にした場合/Ryzen 5)。まだまだ実験的ですが、幾らかアプリケーションが作れるまで来たので、今後簡単な C++ のウェブアプリは clask を使って行こうと思っています。ドキュメントが皆無なのですが、これから整備していく予定です。使用例を5つほど用意していますので、興味のある方は pull-request 頂けると嬉しいです。

clask/example at master · mattn/clask · GitHub

Explore GitHub → Learn & contribute Topics Collections Trending Learning Lab Open source guides...

https://github.com/mattn/clask/tree/master/example
Posted at by