Fork me on GitHub

2009/07/02

はてな
kazuhoさんがやってくれました。
ずいぶん前からjsonをC++でパース(SAXじゃなくてDOM)するのに小さいライブラリないかなーと思ってました。個人的にはjson-cというのを使ってたのですが、幾らか気に入らない所があったりビルドが少し手間だったりしていました。STLしか使わなくてvectorとかmapで表現されるツリー構造な物が欲しいなぁって思ってたんです。
とあるIRCで昨日、kazuhoさんと「ほしいですよねー」という話から始まって、githubにあるjsonxxとかも物色しながら「いいのないねー」とか言ってたらkazuhoさんが「もすこし綺麗に書けそう」って言い出して朝から本格的に書き始めてついさっき出来上がりました。速いw
名前はpicojson
とても小さく、実装コードだと300数十ステップ程です。しかもヘッダファイルだけなので管理が楽です。

試しにwassrのpublicタイムラインをパースしてみました。
コードはこんな感じ。
curlのコードではなく、jsonのパース部分を見てください。
#include <curl/curl.h>
#include "picojson.h"

typedef struct {
    char* data;     // response data from server
    size_t size;    // response size of data
} MEMFILE;

MEMFILE*
memfopen() {
    MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
    mf->data = NULL;
    mf->size = 0;
    return mf;
}

void
memfclose(MEMFILE* mf) {
    if (mf->data) free(mf->data);
    free(mf);
}

size_t
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
    MEMFILE* mf = (MEMFILE*) stream;
    int block = size * nmemb;
    if (!mf->data)
        mf->data = (char*) malloc(block);
    else
        mf->data = (char*) realloc(mf->data, mf->size + block);
    if (mf->data) {
        memcpy(mf->data + mf->size, ptr, block);
        mf->size += block;
    }
    return block;
}

char*
memfstrdup(MEMFILE* mf) {
    char* buf = (char*)malloc(mf->size + 1);
    memcpy(buf, mf->data, mf->size);
    buf[mf->size] = 0;
    return buf;
}

using namespace std;
using namespace picojson;

int
main(int argc, char* argv[]) {
    char error[256];

    MEMFILE* mf = memfopen();
    CURL* curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, "http://api.wassr.jp/statuses/public_timeline.json");
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    if (curl_easy_perform(curl) != CURLE_OK) {
        cout << error << endl;
    } else {
        value v;
        char* ptr = mf->data;
        string err = parse(v, ptr, ptr + mf->size);
        if (err.empty()) {
            array arr = v.get<array>();
            array::iterator it;
            for (it = arr.begin(); it != arr.end(); it++) {
                object obj = it->get<object>();
                cout << obj["user_login_id"].to_str() << ": " << obj["text"].to_str() << endl;
            }
        } else {
            cerr << err << endl;
        }
    }
    curl_easy_cleanup(curl);

    return 0;
}
こんなに短いコードでアプリが作れる!
STLに慣れた人ならイメージ沸くかと思います。すばらしい!
こういうのが欲しかったんです。
ただまだ出来上がったばっかりですしバグはあるかもしれません。また高機能にするつもりもないでしょうから使用目的を選ぶのが先決かと思います。
ライセンスはBSDとの事なので、バイナリ配布も可能です。

share - Revision 34226: /lang/cplusplus/picojson/trunk

picojson

http://svn.coderepos.org/share/lang/cplusplus/picojson/trunk/
ありがたや、ありがたや。

追記
kazuhoさんも記事を書いてますんでそちらも...
Kazuho@Cybozu Labs: 今更 C++ で JSON パーサ「picojson」を書いたわけ
http://developer.cybozu.co.jp/kazuho/2009/07/c-json-picojson.html

2009/06/24

はてな
これはgithubじゃなくても使えるかも
Gitはトランスポート層が選択出来るのは知っていたけど、まさかこんな書き方出来るとは思ってなかった。
以前、こんな記事書きましたが
github.comへのSSH接続にはホスト名"ssh.github.com"、ポート"443"に接続する様に設定します。※このssh.github.comが味噌です。

github.comへのSSH接続にはホスト名"ssh.github.com"、ポート"443"に接続する様に設定します。 ※このssh.github.comが味噌です。

http://mattn.kaoriya.net/software/20081029172540.htm
これ以下の1行で出来る事が分かりました。
# git clone ssh://git@ssh.github.com:443/my-name/my-repo.git
ssh.github.comに443ポートでSSHプロトコルを喋るよ!っていう指定になります。これでcloneしたワーキングツリーでは、以前書いた記事の様にpullはpublic clone、pushはowner cloneというやり方ではなくowner cloneといてpullしている為 .ssh/config ファイルを修正したり
# git remote add origin git@github.com:my-name/my-repo.git
する事もなくいきなりpush出来るになります。
こりゃいいわ。

Version Control With Git Version Control With Git
J Loeliger
Pragma / ¥ 3,142 (2009-06-05)
 
発送可能時間:在庫あり。


はてな
Freenodeのwebchatインタフェースを叩いて通信し、携帯電話からでもIRC出来るCGI書いた。
欲しい人なんかいるんかなーとか思いながら、作ったのでメッセージの送受信しか出来ません。 nick登録もなし、nickの自動割り当てもなしです。
私はこんだけあったら十分なので...。コードはこの辺にあります。
mattn's freenode-mobile-gateway at master - GitHub

IRC gateway for freenode writen in perl.

http://github.com/mattn/freenode-mobile-gateway/tree/master
SoftBank携帯ですが、一応動いてます。欲しい機能があればgithubでforkして下さい。

2009/06/22

はてな
以前こんな記事を書いた事がありました。
Big Sky :: ブラウザを全く使わずにustream.tvを楽しむ方法

映像/音声ですが、Linux版に用意されているスタンドアローン版flashplayerを使っています。ネット上にあるFLVもコマンドラインから起動して閲覧出来ます。

http://mattn.kaoriya.net/web/ustream/20071018005843.htm
その後、ustream.tvで何か変更があり、そのままでは使えなくなってしまったのですが、もういっかいチャレンジしたら見れる事が分かりました。
答えは簡単でobjectタグのパラメータ値を全て足せば再生出来るURLになるという事。
たとえばさっきまで見ていたVimMの動画。
<object
    id="viewer"
    name="viewer"
    width="480"
    height="386"
    allowfullscreen="true"
    codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
ここから
    <param name="movie" value="http://cdn1.ustream.tv/swf/4/viewer.137.swf?varnish=true" />
    <param name="flashvars" value="loc=/&cid=19118&channelid=19118&share=false&group=channel19118&imu=medrect&autoResize=false&localid=4398457344a3f82bac4140&varnish=true&vrsl=c.4.158&" />
ここまで
</object>
これを結合して出来上がったURLにflashplayerスタンドアローン版でアクセスすれば良いのです。
これをshellスクリプトにすると以下の様になりました。
#!/bin/bash

GFLASHPLAYER=/usr/bin/flashplayer
#GFLASHPLAYER=mplayer

if [ "x$1" == "x" ]; then
  echo "usage: `basename $0` [channel]"
  exit
fi

TMP=`mktemp /tmp/XXXXXX`
curl -s "http://www.ustream.tv/channel/$1" | grep "<param" > $TMP
URL=''
while read -r line; do
    if [ ! -z $URL ]; then
        URL="$URL&"
    fi
    URL="$URL`echo \"$line\" | sed  's/^.* value=\"\([^\"]\+\)\".*$/\1/'`"
done < $TMP
rm $TMP

if [ "x$URL" == "x" ]; then
  echo "currently offline?"
  exit
fi
echo playing $URL
$GFLASHPLAYER "$URL"
これでまたブラウザが楽になりました。

はてな
VCだとビルド出来るらしいけど、strawberry perlとかだとコンパイル出来ない。
まずCoro側のpatch。
diff -ur Coro-5.132.orig/Coro/libcoro/coro.c Coro-5.132/Coro/libcoro/coro.c
--- Coro-5.132.orig/Coro/libcoro/coro.c 2008-11-19 11:50:13.000000000 +0900
+++ Coro-5.132/Coro/libcoro/coro.c  2009-06-19 15:01:16.140625000 +0900
@@ -228,6 +228,9 @@
   #if __CYGWIN__
     ctx->env[7] = (long)((char *)sptr + ssize) - sizeof (long);
     ctx->env[8] = (long)coro_init;
+  #elif defined(__MINGW32__)
+    ctx->env[4] = (int)((unsigned char *)sptr + ssize);
+    ctx->env[5] = (long)coro_init;
   #elif defined(_M_IX86)
     ((_JUMP_BUFFER *)&ctx->env)->Eip   = (long)coro_init;
     ((_JUMP_BUFFER *)&ctx->env)->Esp   = (long)STACK_ADJUST_PTR (sptr, ssize) - sizeof (long);
mingw32のjumpbufは4番目がEipで5番目がEspだったはず。
一応手元で動いてます。
あとこのままでも駄目で、ExtUtils::MakeMakerがgccの時に付けてしまう--image-baseオプションがまずい。
アドレスの作り方が Coro::Event であれば Event という文字列に対して上位4バイト、下位4バイトで分けてunpackとかやってござる。
    if ($GCC) {
        my $dllname = $self->{BASEEXT} . "." . $self->{DLEXT};
        $dllname =~ /(....)(.{0,4})/;
        my $baseaddr = unpack("n", $1 ^ $2);
        $otherldflags .= sprintf("-Wl,--image-base,0x%x0000 ", $baseaddr);
    }
なので、Coro::Event が使っている Event というモジュールと --image-base がバッティングして起動時にメモリロケーション不正参照のエラーが起きる。
こちらについては、ExtUtils::MakeMakerに含まれる MM_Win32.pm を修正する。最近の gcc は勝手に --image-base 作ってくれるので、そちらに任せる。
--- MM_Win32.pm.orig    2009-06-22 17:48:43.906250000 +0900
+++ MM_Win32.pm 2009-06-22 17:49:05.703125000 +0900
@@ -307,12 +307,12 @@
 # we try to overcome non-relocateable-DLL problems by generating
 #    a (hopefully unique) image-base from the dll's name
 # -- BKS, 10-19-1999
-    if ($GCC) {
-       my $dllname = $self->{BASEEXT} . "." . $self->{DLEXT};
-       $dllname =~ /(....)(.{0,4})/;
-       my $baseaddr = unpack("n", $1 ^ $2);
-       $otherldflags .= sprintf("-Wl,--image-base,0x%x0000 ", $baseaddr);
-    }
+#    if ($GCC) {
+#      my $dllname = $self->{BASEEXT} . "." . $self->{DLEXT};
+#      $dllname =~ /(....)(.{0,4})/;
+#      my $baseaddr = unpack("n", $1 ^ $2);
+#      $otherldflags .= sprintf("-Wl,--image-base,0x%x0000 ", $baseaddr);
+#    }
 
     push(@m,'
 # This section creates the dynamically loadable $(INST_DYNAMIC)
SYNOPSISの例でもちゃんと動きます。

あとはバグ報告から上手く修正されれば...