2011/04/04


富田さんが書かれた、「CPANモジュールガイド」を献本頂きました。
Growl::Anyを紹介して頂いています。

ざっと目を通しましたが「perlの今」が分かる、読んでてとてもワクワクする本です。各モジュールにサンプルコードが示されていて今ホットなCPANモジュールが150本程紹介されています。perlを知ってる方ならば「あーあのモジュールか。」とニタニタしながら読むことが出来ますし、perlをこれから始める方にとっても「へー最近良く聞くあのモジュールは、こういう機能があって、こう使うんだ。」と分かりやすく書いてあります。
その中にも、サンプルコードで他のモジュールを絡めて書かれている為、アプリケーションを書く上での基礎知識が得られる、持っていて絶対損のないガイドブックになっていると思います。
ボリューム感もあるので、僕もしばらく通勤電車の中でニタニタする事が出来ると思います。


さて、Growl::Anyですが、このモジュールは各OS上で動作するGrowl、もしくはGrowlライクなアプリケーションに対して共通インタフェースを提供するアダプタモジュールです。
Growl::Any - search.cpan.org

Growl::Any - Common interface to Growl

http://search.cpan.org/dist/Growl-Any/
先日gfxさんに大改良して頂き、わかりやすい構成になりました。

実は僕はMac OSXを持っていません。なのでGrowl::Anyが無くても直接困る人でもありません。僕がこれを書いたのは、miyagawaさんなんかが良く書いてるMac OSX上で動くGrowlアプリケーションを、Mac OSXを持ってない人が簡単な修正だけで動かせる様にしてもらいたいと思ったからです。もちろん僕自身もその一人です。
僕はWindows上で動作するGrowl、Growl For Windowsへ通知が出来るCPANモジュールGrowl::GNTPを作っている事もあり、なおかつGrowl For Windowsの中身もまぁまぁ知ってるので(コミット権も持ってるけどあまりコミットしてない苦笑)、たぶんGrowl::AnyのAUTHORとしては適任なのかもしれない。
もちろんGrowlだけじゃなく、あのカッコいいperlアプリケーションをWindowsで動かしたいなと思ってる人は僕だけじゃないと信じています。
そしてMac OSX上で動くイカしたアプリケーションを他のOSで動作させる為のグルー的な役割を、ブログなんかで冗談混じりに紹介し、ポーティングの楽しさ、CPANモジュールの便利さアピールするのが僕の役目なんだろうなと勝手に思っています。

良いアダプタになれればいいなと思います。

さて先日Data::Deduperというモジュールを書きました。
mattn/p5-Data-Deduper - GitHub

remove duplicated item from array

https://github.com/mattn/p5-Data-Deduper
Growlを使ったアプリケーションを書く場合RSSフィードから新着表示したりする訳ですがこの場合、前回フェッチ時のエントリから今回増えたエントリを抽出してGrowl通知する事になります。
そんな場合に役立つんじゃないかと思っています。中身はとても短いコードのモジュールですが、RSSフィードの新着エントリを表示するスクリプトがこんな風に書けます。
#!perl
use strict;
use warnings;
use Encode;
use Data::Deduper;
use XML::Feed;

my $uri = URI->new( "http://api.twitter.com/1/statuses/public_timeline.rss" );

my $dd = Data::Deduper->new(
    expr => sub {
        my ( $a$b ) = @_;
        $a->link eq $b->link;
    },
    size => 50,
);

while (1) {
    my $feed = XML::Feed->parse( $uri );
    for ( $dd->dedup$feed->entries ) ) {
        print encode_utf8 $_->content->body."\n";
    }
    sleep 3;
}
実は、こういったスクリプトを書く際にはこれまでtokuhiromさんが書いたXML::Feed::Deduperを使っていたのですが、RSSフィードの新着確認であればフィード内のエントリ数分だけ管理出来ていれば良くて、特にGrowl通知アプリケーションなんかの場合は大概常駐アプリになるので、DB_Fileを使わなくてもいいんじゃないかという発想で書きました。
まぁ便利かもと思ったのは僕だけかも知れないけど。

こういった、何かの手順を簡素に出来るモジュールをこれからも書いていけたらなぁと思っています。
Posted at by



2011/03/25


V8 JavaScript Engine 最近どのブログでも node.js ばかりでもう飽き飽きしてる皆さんこんばんわ。
node.js 面白いですよね!ェ

node.js ってアプリケーションを作る側(つまりライブラリを使う側)からすると、Web周りの便利なライブラリが既に色々あって、そのライブラリが一体どうやって動いてるのか気にすることってあんまり無いかと思います。

pure javascriptな物ならばコードを読むのは簡単です。ただしやれる事に限りがあります。node.js はGoogle製のJavaScript Engineであるv8をベースに作られているのですが、このv8はアプリケーションに組み込むのに適した構成になっていて、関数テンプレートやインスタンス、プロトタイプという各機能がC++のクラステンプレートで上手く表現出来ているライブラリです。Spidermonkeyも確かに扱うのは簡単なのですが、僕からすると若干C言語臭いというか、JavaScriptとC++との繋ぎが泥臭い感じがしています。
実際、Spidermonkeyを使ったembedがどの様になるかは先日phantomjsのGTK+版であるspecterjsを書いた時のコードを見てもらえると分かるかと思いますが、C言語からJavaScriptの型変換がいかにもフィルタ関数ぽく見えてしまいます。

今日はその辺りを上手く見せているv8で、どうやってembedを行うかを紹介して見ようと思います。

以下の手順にそっていけば、きっと貴方もv8で自作の埋め込みオブジェクトを実装出来る様になるでしょう。

まずv8をインストールしましょう。
v8 - V8 JavaScript Engine - Google Project Hosting

V8 JavaScript EngineV8 is Google's open source JavaScript engine.

http://code.google.com/p/v8/
subversionで以下の様にコードを取得します。 # svn checkout http://v8.googlecode.com/svn/trunk/ v8 ビルドにはSConsが必要です。ubuntuならばaptで、windowsならばオフィシャルからインストーラをダウンロードしてインストールするのが無難かと思います。
SCons: A software construction tool

What makes SCons better? Configuration files are Python scripts--use the power of a real programming...

http://www.scons.org/
参考にするのにまず最初にsamples/shell.ccを見るのをお勧めします。
JavaScriptの対話コンソールプログラムなのですが、グローバルやコンテキスト、関数wrap、スクリプト実行のやり方が分かるかと思います。
コンパイルは # scons sample=shell を実行します。
ここにどうやって自分のオブジェクトを埋め込んでいくかが今回の本筋です。
まず、適当にクラスを作ります。これはv8には依存させません。
class FooImpl {
private:
  std::string value;
public:
  FooImpl() {}
  ~FooImpl() {}
  void SetValue(const std::string v) {
    this->value = v;
  }
  const std::string GetValue() const {
    return this->value;
  }
  void ConcatValue(const std::string v) {
    this->value += v;
  }
};
簡単な文字列を内包するクラスです。GetValue()/SetValue()というアクセサ、ConcatValue()というメソッドを持っています。
これをv8からFooというクラスで表現します。ベースは先ほどのshellに埋め込みます。
まずクラステンプレートを作ります。JavaScriptなのでクラスとは言えど関数テンプレートでしかありません。
// Make class template
v8::Local<v8::FunctionTemplate> clazz = v8::FunctionTemplate::New(Foo_Construct);
clazz->SetClassName(v8::String::New("Foo"));
ここでNewに渡しているFoo_Constructが、v8上で var f = new Foo(); と実行した際に呼ばれるコールバック関数です(関数名は分かりやすくしているだけなのでより良い関数名を付けた方がいいでしょう)。こちらは後で説明します。
次にインスタンステンプレートです。インスタンス化された際の形状を定義します。
// Make instance template
v8::Local<v8::ObjectTemplate> instanceTmpl = clazz->InstanceTemplate();
instanceTmpl->SetInternalFieldCount(1); // Store internal
instanceTmpl->SetAccessor(v8::String::New("value"), Foo_getValue, Foo_setValue); // Accessor
SetInternalFieldCountは何を意味しているかというと、このクラスに内部フィールドを幾つ持たせられるかを教えています。そうです。FooImplのインスタンスですね。
SetAccessorはアクセサ名とゲッタ・セッタ(Foo_getValue()/Foo_setValue())を渡してあげます。こちらも先ほどのFoo_Constructと同様にコールバック関数になっています。これについても後で説明します。
そしてプロトタイプテンプレートを作ります。
// Make method
v8::Local<v8::ObjectTemplate> prototypeTmpl = clazz->PrototypeTemplate();
prototypeTmpl->Set("concat", v8::FunctionTemplate::New(Foo_concat));
global->Set(v8::String::New("Foo"), clazz);
インスタンスとプロトタイプがC++上でも綺麗に色分けされていますね。ここではconcatというメソッドを実装しています。これまでと同様にFoo_concatというコールバック関数を指定します。
最後にglobalに対してFooという名前で関数テンプレートを登録します。
v8との繋ぎ目は以上で完了です。

さて、個々のコールバックを実装して行きましょう。まずコンストラクタ。
static v8::Handle<v8::Value> Foo_Construct(const v8::Arguments& args) {
  FooImpl* foo = new FooImpl();
  printf("construct Foo: %p\n", foo);
  v8::Local<v8::Object> thisObject = args.This();
  thisObject->SetInternalField(0, v8::External::New(foo));
  v8::Persistent<v8::Object> holder = v8::Persistent<v8::Object>::New(thisObject);
  holder.MakeWeak(foo, Foo_Dispose);
  return thisObject;
}
FooImplのインスタンスをSetInternalFieldで設定しています。
このthisをargsから取る辺りも、JavaScriptに似せてあって良いですね。ちなみにArguments::Callee()も存在します。
さて、ここでv8::Persistent::MakeWeak()を呼び出していますね。これはこのインスタンスを保持する予定のホールダーを意味していて、そのホールダーに対してインスタンスが弱参照である事を教えています。またその際、デストラクタをコールバックとして指定しています。
ではデストラクタを実装しましょう。
static void Foo_Dispose(v8::Persistent<v8::Value> handle, void* parameter) {
  FooImpl* foo = static_cast<FooImpl*>(parameter);
  printf("destruct Foo: %p\n", foo);
  delete foo;
  handle.Dispose();
}
MakeWeak呼び出しの第一引数がparameterで渡ってきますので、FooImplのインスタンスを得ます。ここでは単にdeleteしていますが、特別な後処理があるならばここで実装します。
同様の手順でアクセサコールバックを実装します。
static v8::Handle<v8::Value> Foo_getValue(v8::Local<v8::String> propertyName, const v8::AccessorInfo& info) {
  FooImpl* foo = static_cast<FooImpl*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value());
  return v8::String::New(foo->GetValue().c_str());
}

static void Foo_setValue(v8::Local<v8::String> propertyName, v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
  FooImpl* foo = static_cast<FooImpl*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value());
  v8::String::Utf8Value utf8str(value);
  foo->SetValue(*utf8str);
}
GetInternalFieldからコンストラクタで登録していたFooImplのインスタンスを取得します。中身は普通のC++の処理です。
ここまで来るとconcatメソッドも同じですね。
static v8::Handle<v8::Value> Foo_concat(const v8::Arguments& args) {
  FooImpl* foo = static_cast<FooImpl*>(v8::Local<v8::External>::Cast(args.This()->GetInternalField(0))->Value());
  v8::String::Utf8Value utf8str(args[0]);
  foo->ConcatValue(*utf8str);
  return v8::Undefined();
}
第一引数をStringとしてFooImpl::concatを呼び出しています。

以上です。ただこのshellはコンソールから文字列を読み取って実行するループが行われていますのでv8からすると常にbussy状態になっています。共有ライブラリとしてビルド(USING_V8_SHAREDを指定してビルド)していない限りはメインスレッド上で実行されてしまいます。つまりオブジェクトの生死管理が実行されません。なおUSING_V8_SHAREDを指定してビルドされていれば # ./shell --isolate --shell として実行すれば別スレッドとして実行されます。
以前は v8::internal::Heap::CollectAllGarbage(); という関数で強制GCを実施出来ましたが、最近ヘッダファイルから無くなりました。これはおそらく、スレッドが入り乱れても動作しなければならないv8上ではGCのタイミングはユーザが決めるべきではないとの判断だと思っています。その代わりにv8の開発ML上では以下のコードがFAQの様に良く出てきます。
while (!v8::V8::IdleNotification());
v8::V8::IdleNotificationの関数コメントには以下の様に書いてあります。   /**
   * Optional notification that the embedder is idle.
   * V8 uses the notification to reduce memory footprint.
   * This call can be used repeatedly if the embedder remains idle.
   * Returns true if the embedder should stop calling IdleNotification
   * until real work has been done.  This indicates that V8 has done
   * as much cleanup as it will be able to do.
   */
  static bool IdleNotification();
訳すと
組み込み側がアイドル状態であることを意味するオプションの通知。
V8 はメモリの証跡を減らす為に通知を使用します。
これは、組み込み側がアイドル状態である間は何度でも呼び出す事が出来ます。
実際の処理が完了するまでの間、組み込み側が IdleNotification を呼び出すべきでない場合には true を返します。
これは V8 が可能な限り多くのクリーンアップを、この時点で行っていた事を意味します。
なのでGCの動作確認を行うのであればshellのRunShellに以下の行を入れる事になります。 // The read-eval-execute loop of the shell.
void RunShell(v8::Handle<v8::Context> context) {
  printf("V8 version %s\n", v8::V8::GetVersion());
  static const int kBufferSize = 256;
  // Enter the execution environment before evaluating any code.
  v8::Context::Scope context_scope(context);
  while (true) {
    char buffer[kBufferSize];
    printf("> ");
    char* str = fgets(buffer, kBufferSize, stdin);
    if (str == NULLbreak;
    v8::HandleScope handle_scope;
    ExecuteString(v8::String::New(str),
                  v8::String::New("(shell)"),
                  true,
                  true);
    // Notify idle
    while (!v8::V8::IdleNotification());
  }
  printf("\n");
}
さぁ実行しましょう。
# ./shell 
V8 version 3.2.4.1
> var a = new Foo
construct Foo: 0x9526c98
> a.value

> a.value = "foo"
foo
> a.concat("bar")
> a.value
foobar
> a = null
null
destruct Foo: 0x9526c98

ちゃんと動いてますね。デストラクタもきちんと走ってます。
ここまでのコードを以下のGistに貼り付けておきます。
mattn's gist: 885688 — Gist

shell.cc

https://gist.github.com/885688
以上が v8 を使った基本的な embed となります。どうでしょうか?貴方も作れそうな気がしませんか?

ぜひチャレンジしてみて下さい。なお、だいぶ昔ですが v8 から twitter や wassr を操作出来る shell の改良物を作った事があったので参考URLとして記しておきます。
/lang/cplusplus/twitter-v8 – CodeRepos::Share – Trac

twitter-v8

http://coderepos.org/share/browser/lang/cplusplus/twitter-v8
面白い物が出来たらぜひ公開してみて下さい。
Posted at by



2011/03/19


以前、kazuhoさんが書いたpicojsonの紹介をさせて頂いたのですが、あれから何度も使わせて頂いていてkazuhoさんのいる方角には足を向けて寝られないmattnです。
Big Sky :: ヘッダファイルだけでC++から使えるJSONパーサ「picojson」が凄い!

kazuhoさんがやってくれました。ずいぶん前からjsonをC++でパース(SAXじゃなくてDOM)するのに小さいライブラリないかなーと思ってました。個人的にはjson-cというのを使ってたのですが...

http://mattn.kaoriya.net/software/lang/c/20090702153947.htm
なんど見ても綺麗なコードです。
で、先日ちょっと出張があって新幹線に乗る事があり、ちょっとルールを決めて僕にも同じ物が書けるか試してみました。
以下ルール。
  • picojson相当のjsonパーサをC++で書く
  • インターネット等の外部リソースは見てはいけない(picojsonも見ちゃダメ)
  • 見て良いのは/usr/include以下だけ
  • 制限時間は新幹線に乗っていられる間だけ
このルールで新幹線に乗った途端、ノートPCを開いて書き始めました。いろいろ書いては直してを繰り返し、出来上がった物を公開しておきます。おそらく、picojsonと同じ様な動きをします。
但し、後からpicojsonを見て気づいたのですが、"\uXXXX"といった文字列表記に対応出来ていません。まぁもう外部リソースを見てしまったので、これから対応するつもりはありません。
一応、マルチブートのノートPCだったのでWindowsでもコンパイル出来る様にしてあります。
あと、オブジェクトのリテラルで最後がカンマで終わる様なフォーマットも#define宣言でパース出来る様にしました。さらに関数テンプレートが使えないVC6でもビルド出来る様になっています。(使い方が若干異なりますが)

picojsonと同じようにヘッダファイルだけで使えます。ただしkazuhoさん程のC++erでは無いので抜けがあるかもしれませんが...苦笑。

githubに適当にアップしておきました。
mattn/minijson - GitHub

simple C++ json library. (clone of kazuho/picojson)

https://github.com/mattn/minijson
ネタですが、よろしければ話題のネタ程度にどうぞ。
Posted at by