2017/05/10


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

結構前からあった様だけど気付いて無かった。

GitHub - DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C

README.md cJSON Ultralightweight JSON parser in ANSI C. Table of contents License Usage Welcome to c...

https://github.com/DaveGamble/cJSON

特徴は以下の通り。

  • 分かりやすい API
  • MIT ライセンス
  • スレッドセーフ
  • ANSI C (もしくは C89, C90) で書かれている

Not C++ で使える JSON パーサとしては分かりやすい API で良いと思います。参照カウンタを持っているのでメモリの解放もルートオブジェクトの破棄だけで行えます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"

int
main(int argc, char* argv[]) {
  cJSON *root = cJSON_CreateObject(), *arr, *item; 

  cJSON_AddNumberToObject(root, "count"1);
  cJSON_AddItemToObject(root, "items", arr = cJSON_CreateArray());
  cJSON_AddItemToArray(arr, cJSON_CreateString("こんにちわ世界"));
  cJSON_AddItemToArray(arr, cJSON_CreateFalse());
  cJSON_AddItemToArray(arr, cJSON_CreateNull());
  puts(cJSON_PrintUnformatted(root));
  cJSON_free(root);
  
  root = cJSON_Parse("{\"count\":1, \"items\":[\"こんにちわ世界\",false,null]}");
  arr = cJSON_GetObjectItem(root, "items");
  cJSON_ReplaceItemInArray(arr, 0, cJSON_CreateString("こんにちわ日本"));
  cJSON_ArrayForEach(item, arr) {
    puts(cJSON_Print(item));
  }
  cJSON_free(root);
  return 0;
}

欲を言うとシリアライズ時にメモリを確保しない API が欲しいです。


2017/04/09


最近、我が家にドルチェグストがやってきた。

コーヒーは良く飲むけど、自分で入れるし別に要らんわー。と思ってたんだけど、届いてみるとこれがなかなか。安定した味が毎回出せるので気付いたらこれで入れたコーヒーばかり飲んでる。同じ物ばかり飲んでたら飽きるかもと思ったので楽天で6種類セットを買った。

  • ティーラテ
  • カフェオレカフェインレス
  • カフェオレ
  • ラテマキアート
  • レギュラーブレンド
  • インテンソ

気を付けた方がいいなと思ったのは、カプセルの種類の中にはコーヒーとミルクの2個を一度に使う物があり、気付かずミルクだけで入れてしまうと「なんだよドルチェグストってこういう物なの?」って気分になってしまう。(僕がそうだ)

原稿書いたりするときに、ネタで頭いっぱいになっててコーヒー入れるのも面倒になってしまう事もあるけど、これ水入れて30秒で熱湯が出る様になってるので1分あれば美味しいコーヒーが入れられる。あと洗い物が出ない。これ大事。嫁が買うって言い出した物だけど、僕の方が飲んでる。思ってたより良かった。


2017/04/06


今日こんなツイートをした。

qt_luigi さんからどうしてかを聞かれたので説明したいと思います。

golang では宣言した位置で初めて自動変数としてメモリが確保され、ゼロクリアされます。

for i := 0; i < b.N; i++ {
    var foo Foo
    bar, err := doSomething()
    if err != nil {
        continue
    }
    foo.v = bar
    fmt.Fprintln(ioutil.Discard, foo)
}

なので例えばこの様なコードで doSomething() が err を返した場合、foo が無駄に初期化されてしまうのです。

本当にそうなのか、以下のベンチマークを見て貰えると分かります。

package var_test

import (
    "errors"
    "fmt"
    "io/ioutil"
    "testing"
)

type Foo struct {
    v *Bar
    b [1000]int64
}

type Bar struct {
}

func doSomething() (*Bar, error) {
    return nil, errors.New("bad some")
}

func BenchmarkVar1(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var foo Foo
        bar, err := doSomething()
        if err != nil {
            continue
        }
        foo.v = bar
        fmt.Fprintln(ioutil.Discard, foo)
    }
}

func BenchmarkVar2(b *testing.B) {
    for i := 0; i < b.N; i++ {
        bar, err := doSomething()
        if err != nil {
            continue
        }
        var foo Foo
        foo.v = bar
        fmt.Fprintln(ioutil.Discard, foo)
    }
}

通るはずのない所に Println を書いたのはコンパイラが最適化して消し去ってしまわない様にです。(本当に消し去るかは未確認)

goos: windows
goarch: amd64
pkg: github.com/mattn/go-sandbox/var
BenchmarkVar1-4     10000000           226 ns/op           0 B/op          0 allocs/op
BenchmarkVar2-4     2000000000           1.26 ns/op        0 B/op          0 allocs/op
PASS
ok      github.com/mattn/go-sandbox/var 5.222s

ちょっと大げさに int64 変数が1000個保持されるような struct で確認しているので180倍近い差が出ていますが、少し大きめの構造体でも幾らかは差が出てしまいます。early return は golang の良い文化ではありますが、さらに変数の宣言位置も気を付けておくとよりパフォーマンスの良いアプリケーションになっていくでしょう。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。