2017/03/07

Recent entries from same category

  1. RapidJSON や simdjson よりも速いC言語から使えるJSONライブラリ「yyjson」
  2. コメントも扱える高機能な C++ 向け JSON パーサ「jsoncpp」
  3. C++ で flask ライクなウェブサーバ「clask」書いた。
  4. C++ 用 SQLite3 ORM 「sqlite_orm」が便利。
  5. zsh で PATH に相対パスを含んだ場合にコマンドが補完できないのは意図的かどうか。

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

ずいぶんと前から GitHub Trend の C++ 言語枠には登場していましたが、このコーナーでレビューしてませんでした。

GitHub - nlohmann/json: JSON for Modern C++

JSON for Modern C++

https://github.com/nlohmann/json

特徴(目指すところ)は以下の通り。

  • Python の様な操作感で、ファーストクラスオブジェクトの様に扱える事
  • シングルヘッダで他のライブラリに依存しない事
  • テストカバレッジ 100% を目指す

触ってみた感じ picojson に近いです。picojson よりもオペレータが沢山定義されているのでバイナリにした際には picojson よりも大きくなるかもしれません。今日はこの json.hpp を使って、wandbox の shebang コマンドを作ってみました。

wandbox は画面で入力したソースコードを多種多様なコンパイラやインタプリタを使って実行してくれるウェブサービスです。API が公開されているので扱いやすいインタフェースを自分で作る事ができます。

wandbox/API.rst at master - melpon/wandbox - GitHub

API API home is melpon.org/wandbox/api GET /list.json List compiler informations. Parameter Nothing....

https://github.com/melpon/wandbox/blob/master/kennel2/API.rst
#include <iostream>
#include <fstream>
#include "json.hpp"
#include <curl/curl.h>

size_t
write_cb(char *ptr, size_t size, size_t nmemb, std::string *strm) {
  size_t len = size * nmemb;
  strm->append(ptr, len);
  return len;
}

int
main(int argc, char* argv[]) {
  if (argc < 3) {
    std::cerr << "usage: " << argv[0] << " [compiler] [file] [arguments...]" << std::endl;
    return 1;
  }
  std::stringstream ss;
  if (std::string(argv[2]) != "-") {
    std::ifstream in(argv[2], std::ifstream::in);
    std::string line;
    std::getline(in, line);
    ss << in.rdbuf();
  } else {
    ss << std::cin.rdbuf();
  }

  nlohmann::json obj;
  obj["code"] = ss.str();

  ss.str("");
  ss.clear(std::stringstream::goodbit);
  obj["compiler"] = argv[1];
  for (int i = 3; i < argc; i++) {
    if (i > 3)  ss << "\n";
    ss << argv[i];
  }
  obj["runtime-option-raw"] = ss.str();

  ss.str("");
  ss.clear(std::stringstream::goodbit);
  ss << obj;

  std::string chunk;

  CURLcode ret;
  CURL *curl = curl_easy_init();
  if (curl == NULL) {
    std::cerr << "curl_easy_init() failed" << std::endl;
    return 1;
  }
  std::string data = ss.str();

  struct curl_slist *headerlist = NULL;
  headerlist = curl_slist_append(headerlist, "Content-Type: application/json");

  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
  curl_easy_setopt(curl, CURLOPT_URL, "http://melpon.org/wandbox/api/compile.json");
  curl_easy_setopt(curl, CURLOPT_POST, 1);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
  ret = curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  if (ret != CURLE_OK) {
    std::cerr << "curl_easy_perform() failed" << std::endl;
    curl_easy_strerror(ret);
    return 1;
  }

  obj = nlohmann::json::parse(chunk);
  try {
    std::cout << obj["program_message"].get<std::string>();
  } catch(std::exception& e) {
    std::cerr << obj["compiler_message"].get<std::string>() << std::endl;
  }

  int code;
  ss.str("");
  ss.clear(std::stringstream::goodbit);
  ss << obj["status"];
  ss >> code;
  return code;
}

使い方は shebang として設定するだけで、第一引数にコンパイラもしくはインタプリタ名を設定します。

#!/usr/bin/wandbox-run clang-head

#include <stdio.h>

int
main(int argc, char* argv[]) {
  puts("hello clang");
  return 0;
}

こんな風に書いて実行権限を付けて実行すれば hello clang が表示されます。C言語のソースなのにコンパイラをインストールしなくても実行できて便利ですね(そうでもない)。なお扱えるコンパイラやインタプリタは wandbox から参照して下さい。ソースコードは以下に置いておきます。

mattn/wandbox-run · GitHub
https://github.com/mattn/wandbox-run
Posted at by