株式会社クリアコード > ククログ

ククログ

タグ:

GObject Introspectionを使ったRubyバインディングの開発方法

日本ではだいぶGObject Introspectionに詳しい方だと思っている須藤です。

バインディングの開発には5年くらい前からバインディングの開発にGOject Introspectionが有用だと思っています。

2013年には各種バインディング開発方法についてまとめたりGObject Introspection対応ライブラリーの開発方法を導入部分だけ説明したりしました。

2016年にはRubyKaigi 2016で各種バインディング開発方法を紹介しました。

2017年には名古屋Ruby会議03でGObject Introspectionを使ったバインディングの開発方法のRubyレベルの部分だけを紹介しました。

そして今年、1からGObject Introspection対応ライブラリーを開発する方法を1つずつ説明する文書をまとめました!OpenCVをGObject Introspectionに対応させています。GObject Introspection対応ライブラリーを開発するための日本語の文書としては一番よい文書になっているはずです。

この文書はRubyDataのリポジトリーで管理しています。RubyDataというのはSpeee@mrknさんが始めた取り組みです。Ruby用のデータ処理ツールを開発する人たちとそのツールを使う人たちを増やすことを目指しています。

これまで、RubyKaigi 2017でワークショップを開催したり、サイトで関連情報をまとめたりしていました。RubyKaigi 2018でもワークショップを開催する予定です。

Rubyで使えるデータ処理関連のライブラリーが増えるとRubyでできることが増えます。バインディングの開発はデータ処理関連のライブラリーを増やす1つのやり方です。GObject Introspectionが有用なケースもあるはずです。ぜひ、この文書を活用してRubyで使えるデータ処理関連のライブラリーを増やしていきましょう。

興味のある人はRed Data Toolsチャットルーム(オンライン)や東京で開催している開発イベント(オフライン)にどうぞ!

タグ: Ruby
2018-03-28

沖縄Ruby会議02 - Red Data Tools #okrk02

沖縄から東京へ帰る飛行機を待っている須藤です。25分遅延しているのでこれを書いています。

沖縄Ruby会議02でゲストスピーカーの1人としてRed Data Toolsの話をしました。

関連リンク:

内容

Red Data Toolsに参加する人(Rubyでもっと便利にデータ処理できるようになるために取り組む人)が増えるといいなぁと思って次のことを紹介しました。

  • Red Data Toolsのポリシー

    • こういうポリシーなら一緒に開発したい!と思ってもらえるかも!
  • Red Data Toolsで開発しているコード

    • こういうコードを一緒に開発したい!と思ってもらえるかも!

私達はインターネットを通じてやりとりできます。場所も時間も超えて一緒に開発できます。まずはチャットでなにに取り組むか相談するところから始めましょう。沖縄Ruby会議02に参加したみなさん、待っていますよ!!!

スライド中で紹介したコードや省略したコードは前述の「リポジトリー」の中にちゃんと動くコードとしてまとまっています。コードに興味がでてきた人はリポジトリーの中ものぞいてみてください。

スライドサイズ

会場が大学だったのでプロジェクターの出力の縦横比は4:3だと思っていたんですが、現地について確認してみたら16:9でした。琉球大学すごい!

Rabbit 2.2.2(未リリース)ではスライドの縦横比が16:9でもいい感じになるようになっています。Rabbit Slide Showgem pushすればスライドを公開できるかっこいいスライド共有サービス)も16:9に対応していて、前述の埋め込んだスライドも16:9になっています。

Rabbitは内容(ソース)と描画(テーマ・描画システム)を分離する設計になっています。(現実では内容の中に描画のための情報をちらちら入れちゃうけど。)

そのため、スライドの縦横比を4:3から16:9に変更したい場合はrabbitコマンドの引数に--size 800,450を指定するだけです。

% rabbit --size 800,450 red-data-tools.rab

あとはその縦横比に合わせてRabbitがいい感じにレイアウトを調整します。便利ですね!

まとめ

沖縄Ruby会議02でRed Data Toolsに参加しようぜ!という話をしました。

Rabbitの最近の新機能を自慢しました。

タグ: Ruby
2018-03-11

Rubyとクリアコード #ruby25th

これはRuby25周年へのメッセージです。

クリアコードの社長の須藤です。そんなにコミットしていないのであんまり自分から言わないのですが、Rubyのコミット権を持っています。

同い年のRubyコミッター8人の中では一番最初にコミット権をもらいました。2004年の1月のことなので、なんともう14年前!当時は大学生だったのですが、すごくドキドキしたことを覚えています。私がコミット権をもらったのは自分が作っているライブラリーがRuby本体に取り込まれたからなんですが、会ったこともない人がしっくりくると推薦してくれたことがうれしかったです。Rubyが使いやすいなぁと思って使っていたので、他のRubyistからしっくりくると思ってもらえる使い勝手のライブラリーを作れているのがわかったのが嬉しかったんでしょうねぇ。

クリアコードが始まったのは私が社会人になった2006年の7月で、私は(たしか)9月からクリアコードに合流しました。私の社会人歴のほぼすべてはクリアコードでのものです。

クリアコードはフリーソフトウェアを推進するのが一番大事な会社であって、Rubyを応援するのが一番大事な会社ではないので、クリアコードのメンバーみんながRubyistというわけではありません。クリアコードを始めた当時、Rubyistは私だけでした。

私はフリーソフトウェアも推進したいしRubyも応援したかったので、なにかしらRubyを活かせる場所をみつけてRubyを活用していました。たとえば、独立行政法人情報処理推進機構(IPA)平成20年度オープンソフトウェア利用促進事業(リンク切れ)が「迷惑メール対策でなにか」みたいなテーマで募集していたときは、「Rubyを組み込んだ迷惑メール対策システム」を応募しました。それに採択されてお金をもらって開発したのがmilter managerというフリーソフトウェアです。「Rubyを組み込むと動的にいろいろできて捗るよ!」というようにRubyを活かす場所を考えました。

milter managerの開発を始めたのが10年前なのですが、実は、milter managerきっかけでいくつかRubyをよくしたことがあります。1つがメモリーリークの修正で、もう1つが拡張ライブラリーのメモリー使用量を抑えやすくするAPIの追加です。

前者は再現スクリプトを作るのに数週間とか使った気がする(もちろん業務時間内でやっていた)ので、なかなか大変だったなぁという記憶があります。作業していたのは私ではないですが。

後者はmilter managerを開発し始めてから8年後の東京Ruby会議11での発表がきっかけで話が進みました。なにがつながるかわからないものですね。

直接お金を稼いでいるわけではないですが、仕事ですごくRubyを活用している例があります。それはRabbitという私がRubyを使って開発しているプレゼンテーションツールです。

クリアコードは「お客さんを探す」ではなく「お客さんに見つけてもらう」という仕事の探し方をしているので、クリアコードがいろいろ情報発信をすることはお金を稼ぐためにとても大事なことです。ここにいろいろ記事を書くこともそうですし、イベントで発表することもそうです。そして、イベントを発表するときに役立つのがRabbitです。

Rabbitは私が大学にいたときに研究関係の発表をするために作り始めたものです。私にとってはRubyで作ることが大事だったので、プレゼンテーションツールを作るためにRubyでできないことがあれば、それらをRubyでできるようにしながら作ってきました。たとえば、PDF出力機能GUI・画像処理・マルチメディア機能などです。これらの機能があるからRubyを使っているという人がいるといいな。

Rabbitはすごくヒットしているツールではありません。RubyのイベントでもRabbitを使っている人は極少数派です。ただ、まつもとさんがMagicPointからRabbitに乗り換えたので、ヒットしていなくても私は満足です。みんながまつもとさんのいい話を聴けるのは私のおかげでもあるはず!(RubyKaigi 2017でまつもとさんに教えてもらったRabbitの問題の修正は25周年イベントには間に合わなかったなぁ。残念。)

本当のところを言うと、私は自分が使うために作っているのでユーザーが私だけでも満足だったりします。言い方を変えると、ヒットしていようがしていまいが私は別にどうでもいいです。そういえば、ここ数年、なぜか私以外のクリアコードのメンバーもRabbitを使っているのが不思議です。特に強制していないはずなんですが。。。

Ruby25周年ということで、クリアコードでのRubyの関わり方を一部紹介しました。Rubyを積極的に使っていく(人がいる)し、Rubyを使う中で得られた知見はRuby本体にフィードバックするし、まつもとさんのプレゼンをツールでサポートする、とかやっています。そうそう、RubyKaigiのスポンサーとしてお金を出すというのもやっていました。

これからも引き続き同じような感じでRubyと関わっていくつもりです。近い将来、Red Data Tools関連のことでも稼げるようになるといいなぁと思っています。今はあまり稼げていませんが、開発中に気づいたKeyErrorの改良案をRuby 2.6に入れたり、Rubyのcsvをよくしたり、Rubyでできることを増やしたり、といった点でRubyをよくすることはでき始めています。

タグ: Ruby
2018-02-23

RubyKaigi 2017 - Improve extension API: C++ as better language for extension #rubykaigi

RubyKaigi 2017で拡張ライブラリー関連の話をしてきた須藤です。クリアコードはシルバースポンサーとしてRubyKaigi 2017を応援しました。

関連リンク:

内容

C++11を活用するともっと拡張ライブラリーを書きやすくなるよ、という内容でした。詳細は事前情報を読んでください。

個人的には今後の拡張ライブラリー開発にプラスになるとても実用的な話をしたつもりだったのですが、あまり反響がなかったので、よさを伝えきれなかったのだと思います。残念。

誰も質問してくれなかったので付録の生のC API以外で拡張ライブラリーを書く方法の比較はお蔵入りになりました。聞きたい人はなにかのイベントに呼んでください。

Red Data Tools

発表の反響はあまりなかったですが、Red Data Toolsの反響はありました。

RubyData Workshop in RubyKaigi 2017の1つとしてSpeee@hatappiさんとRed Data Toolsの紹介をしました。(@hatappiさんがメインで説明・進行をして、私はたまに補足するスタイル。)

来週の火曜日(9月26日)の夜にSpeeeさんで開催するRed Data Toolsの開発イベントOSS Gate東京ミートアップ for Red Data Tools in Speeeの参加者が増えました。オンラインで相談する場所はGitterのred-data-tools/jaにあるので、開発イベントに参加できない人も一緒に開発しましょう!

Rubyでデータ処理できるようにしたいみなさん、一緒に開発していきましょう!

自分達は開発できない・開発する時間がないけどお金は出せるという場合は、クリアコードに開発の仕事を依頼するというやり方があるのでお問い合わせください。

OSS Gate

Red Data Toolsと同じようにOSS Gateも反響がありました。RubyKaigi 2017 前夜祭安川さんが紹介してくれたのと、Speeeさん・永和システムマネジメントさん・ドリコムさん・ピクシブさんのブースにチラシを置いてもらったのが大きいです。ありがとうございました!

おかげで広島でもOSS Gateの活動を始められそうです。Gitterのoss-gate/hiroshimaで相談しているので、広島でもOSSの開発に参加する人が増えるとうれしい人は参加してください。

全国のOSS Gateワークショップ開催情報は次の通りです。近隣で開催している場合はぜひビギナー・サポーターとして参加してください。

まとめ

RubyKaigi 2017の発表内容と成果を紹介しました。

タグ: Ruby
2017-09-22

事前情報:RubyKaigi 2017 - Improve extension API: C++ as better language for extension #rubykaigi

結構Rubyの拡張ライブラリーを書いている方だと思っている須藤です。RubyKaigi 2017で拡張ライブラリー関連の話をする予定です。RubyKaigi 2017で私の話をより理解できるようになるために内容を紹介します。

関連リンク:

背景

たくさんRubyの拡張ライブラリーを書いてきた経験を活かして拡張ライブラリーのC APIをもっとよくできないかについて考えています。バインディングについてはRubyKaigi 2016で紹介したGObject Introspectionベースがよいと思っていますが、バインディングではないただの拡張ライブラリーはC++を活用するのがよさそうだと思っています。なぜC++を活用するのがよいと思うかは私が実現したいことに関わっています。

実現したいこと

私が実現したいことはC/C++のライブラリーを使ってRubyスクリプトを高速化することです。具体的には、xtensorというC++で実装された多次元配列ライブラリーを使ってRubyスクリプトを高速化したいです。

1つ1つの機能に対してバインディングを用意してRubyレベルで組み合わせるやり方もあります。ただ、場合によっては機能を実行する毎にRubyレベルに戻ってくるオーバーヘッドを無視できないことがあります。あると思っています。まだ実際に遭遇したわけではありませんが。

あまりよい例ではありませんが。。。たとえば、GPU上で演算をする機能があって、その機能を実行する毎にGPU上にデータを転送して演算をして演算結果をまた転送しなおすとしたら、オーバーヘッドは無視できません。まぁ、この場合は、拡張ライブラリーで一連の演算をまとめるよりも、必要な間はずっとGPU上にデータを置いておく機能をRubyレベルに用意する方が汎用的でよさそうです。最近、Apache ArrowにGPU上のデータを管理する機能が入ったので、この場合はApache Arrowと連携する機能を用意するのがよさそうです。

C++11を活用するやり方

C/C++で書かれたライブラリーを使った拡張ライブラリーを書くにはRubyが提供するC APIを使います。このC APIは悪くないのですが、Cなので書いているときに書きにくいなぁと感じることがあります。

たとえば、メソッドを定義するときに関数定義とメソッドの登録が離れるのが不便だなぁと感じます。次のようにrb_hello()の定義とrb_define_method()の呼び出しが離れています。

#include <ruby.h>

static VALUE
rb_hello(VALUE self)
{
  return rb_str_new_cstr("Hello");
}

void
Init_hello(void)
{
  VALUE rb_cHello = rb_define_class("Hello", rb_cObject);

  rb_define_method(rb_cHello, "hello", rb_hello, 0);
}

あとは、例外が発生したときにキレイにリソースを開放するためにrb_rescue()rb_ensure()を使うときが面倒です。

他には、RubyのオブジェクトをCの値に変換する各種APIに統一感がないのも地味に使い勝手が悪いです。たとえば、Rubyのオブジェクトをboolに変換するにはRTEST()を使いますし、intに変換するにはNUM2INT()を使います。

C++11以降の最近のC++を使うことで今のC APIをもっと便利にできます。

たとえば、C++11にはラムダ式があります。これを活用することで次のようにdefine_method()で直接メソッドを定義できます。これはExt++というライブラリーを使っています。

#include <ruby.hpp>

extern "C" void
Init_hello(void)
{
  rb::Class("Hello").
    define_method("hello",
                  [](VALUE self) { // ←ラムダ式
                    return rb_str_new_cstr("Hello");
                  });
}

Rubyでdefine_methodを使うと次のような書き方になりますが、少し似ていますね。

class Hello
  define_method(:hello) do
    "Hello"
  end
end

C++11を活用するやり方のメリット・デメリット

このようなC++11を活用するやり方のメリットは次の通りです。

  • より完結に書ける
    • ラムダ式:その場で関数を定義できる
    • auto:型推論を使うことで必要な型だけ書けばすむようになる
    • range-based for loop:従来のfor (int i = 0; i < n; ++i)だけでなく、Rubyのeachのように自分でインデックスを回さなくてもforを使える
  • 既存のRubyのC APIも使える
    • 拡張ライブラリーを書いたことがある人なら徐々に便利APIに移行できる
  • C/C++のライブラリーをそのまま使える
    • (Ruby用のじゃなくてC++用の)バインディングを用意する必要がない
    • たとえば、Rustを使うならバインディングを用意する必要がある
  • デバッグしやすい
    • 普通にGDB/LLDBを使える
  • 最適化しやすい
    • Feature #13434 better method definition in C API」関連のAPIの改良にも使えるかも

簡単に言うと、既存の資産を活用しつつ便利になるよ、という感じです。

一方、デメリットは次の通りです。

  • C++には難しい機能がたくさんあるので油断するとメンテナンスしにくくなる
    • たとえばテンプレート
  • ビルドが遅い
  • C++の例外とRubyの例外は相性が悪い
    • Rubyの例外はsetjmp()/longjmp()で実装されているのでRubyの例外が発生すると、スコープを抜けたC++のオブジェクトのデストラクターが呼ばれない
  • 古い環境だとC++11を使うのが大変
    • たとえば、CentOS 6の標準パッケージのg++では使えない

例外に関してはライブラリーでカバーする方法があるので、基本的にはC++に起因するデメリットになります。

このようなデメリットはあるものの、適切に使えば十分メリットの方が大きくなると思っています。Ruby本体にC++のAPIがあってもいいのではないかと考えていた時期もあったのですが、RubyKaigi 2017の資料をまとめていたら少し落ち着いてきて、今は、もう少し検討してよさそうなら提案しよう、くらいに思っています。

C++11を活用する以外のやり方

以前からもっと便利に拡張ライブラリーを書きたいという人たちがいます。私はC++11を活用するアプローチがよいと思っていますが、他のアプローチも紹介します。

大きく分けて3つのアプローチがあります。

  • Rubyを拡張して拡張ライブラリーも書けるようにする
  • C以外の言語で拡張ライブラリーを書けるようにする
  • C APIを使いつつ便利APIで改良する

最後のアプローチがC++11を活用するアプローチです。

最初の「Rubyを拡張する」アプローチはRubexのアプローチです。Rubyに追加の構文を導入して拡張ライブラリーも書けるようにしようというアプローチです。RubyKaigi 2017で発表があります。

Pythonでは同様のアプローチで成功しているプロダクトがあります。それがCythonです。CythonはPythonでデータ分析をする界隈では広く使われています。(使われているように見えます。)

私はこのアプローチはあまりスジがよくないと感じています。理由は次の通りです。

  • 一見使いやすそうだが結局使いにくいAPI
    • Rubyっぽい構文なのでRubyユーザーにも使いやすいような気がするが、実際はところどころに違いがあって、結局Rubyではない言語なので使いにくさにつながる
    • Rubyっぽく書けるのでCの知識は必要なさそうに思えるが、libffiを使うときのように結局Cの知識は必要になる
    • RubyとCだけでなくRubexの知識も必要になり、結局覚えることは結構多い
  • メンテナンスが大変
    • Rubyが新しい構文を導入したら追従する必要がある
    • Rubyの構文と衝突しないようにRubexを拡張していく必要がある
  • デバッグが大変
    • Rubexが生成したCのコードをベースにデバッグする必要がある

ただ、Cythonが成功している事実と、ちょっとした拡張機能を書く分にはRubyの知識と少しのRubexの知識だけでよい(Cのことはあまり知らなくてよい)という事実があるので、もしかしたらそんなにスジは悪くないのかもしれません。数年後も開発が継続していたら再度検討してみたいです。

2番目の「C以外の言語を使う」アプローチはHelixのアプローチです。Rustで拡張ライブラリーを書けるようにしようというアプローチです。RubyKaigi 2017で発表があります。

私はC/C++のライブラリーを使いたいのでこのアプローチは私の要件にはマッチしないのですが、高速化のために処理を全部で自分で実装する(あるいはRustのライブラリーを活用して実装する)場合はマッチしそうな気がします。

このアプローチのメリットは、Rustを知っているならCで書くよりもちゃんとしたプログラムをすばやく書けることです。デメリットはRubyのC APIのフル機能を使えない(使うためにはメンテナンスを頑張る必要がある)ことです。たとえば、Ruby 2.4からrb_gc_adjust_memory_usage()というAPIが導入されましたが、Rustからこの機能を使うためにはバインディングを用意する必要があります。つまり、RubyのC APIの進化にあわせてメンテナンスしていく必要があります。

C++を活用する方法

最後に現時点でC++を活用する方法を紹介します。

1つがRiceを使う方法です。RiceはC++で拡張ライブラリーを書けるようにするライブラリーです。10年以上前から開発されています。C++でPythonの拡張ライブラリーを書けるようにするBoost.Pythonに似ています。

例外の対応やメソッドのメタデータとして引数のデフォルト値を指定できるなど便利な機能が揃っています。ただし、昔から開発されているライブラリーで現在はメンテナンスモードなため、C++11への対応はそれほど活発ではありません。メンテナーは反応してくれるので自分がコードを書いて開発に参加するのはよいアプローチだと思います。

もう1つがExt++を使う方法です。Ext++もC++で拡張ライブラリーを書けるようにするライブラリーです。私が作り始めました。RiceはRubyのCのオブジェクトをすべてラップしてC++で自然に扱えるようにするようなAPIです。つまり、できるだけRubyのC APIを使わずにすむようにしたいようなAPIです。私は、もっとC APIが透けて見えるような薄いAPIの方が使いやすいのではないかという気がしているので、その実験のためにExt++を作り始めました。薄いAPIの方が使いやすいのか、結局Riceくらいやらないと使いやすくないのかはまだわかっていません。Red Data Toolsのプロダクトで使って試し続けるつもりです。

まとめ

RubyKaigi 2017で拡張ライブラリーを書きやすくするためにC++がいいんじゃない?という話をします。

おしらせ

去年もスポンサーとしてRubyKaigiを応援しましたが、今年もスポンサーとしてRubyKaigiを応援します。去年と違って今年はブースはありません。懇親会などで見かけたら声をかけてください。拡張ライブラリーに興味のある人と使いやすいAPIについて話をしたいです!

あと、RubyKaigi 2017の2日目の午後に通常のセッションと並行して「RubyData Workshop」というワークショップが開かれる予定です。まだRubyKaigi 2017のサイトには情報はありませんが、時期に情報が載るはずです。このワークショップではPyCallRed Data Toolsの最新情報を手を動かして体験することができます。Rubyでデータ処理したい人はぜひお越しください!

つづき: 2017-09-22
タグ: Ruby
2017-09-11

RubyKaigiのCFPへの応募例 #rubykaigi

須藤です。関西Ruby会議2017が終わってからRubyKaigi 2017の発表を応募しました。

RubyKaigi 2017CFPがでています。CFPとはもともと(?学会の文脈で)はCall For Papersの略ですが、RubyKaigiの文脈ではCall For Proposalsの略で、「RubyKaigiでの発表を募集しています」という意味です。RubyKaigi 2017の発表の応募は6月17日まで受け付けています。

応募する人が増えるといいなぁと思うので、実際の応募例として私のRubyKaigi 2015からRubyKaigi 2017の分の応募内容を紹介します。どうしてRubyKaigi 2015の分からかというと、CFPアプリケーションができて応募の記録が残るようになったのがRubyKaigi 2015からだからです。

項目

実際の応募内容を紹介する前に、どのような項目を書く必要があるかを紹介します。

  • Title: 発表のタイトル(60文字以下。英語。)
  • Abstract: 発表の概要(600文字以下。英語。)
  • Details: 発表の詳細(日本語可日本語でも検討してもらえるが、RubyKaigiは国際カンファレンスなので英語が望ましい。)
  • Pitch: どうして自分がRubyKaigiでこの発表をするべきなのかの説明(日本語可日本語でも検討してもらえるが、RubyKaigiは国際カンファレンスなので英語が望ましい。)

TitleにはRubyKaigiに参加する人が「どの発表を聞くか」をパッと選ぶときに参考になりそうなものを書きます。

AbstractにはRubyKaigiに参加する人が「どの発表を聞くか」を検討するときに参考になりそうな説明を書きます。

Detailsには発表を選考する人が「どの発表を選ぶか」を検討するときに参考になりそうな発表の説明を書きます。発表を聞いた人が何を得られる発表なのかを書くとよいでしょう。

Pitchには発表を選考する人が「どの発表を選ぶか」を検討するときに参考になりそうな発表者の説明を書きます。たとえば、「私が実装した人なので私が一番詳しいです。なので、私が話すのが一番いいんです!」ということを書きます。

これらのことは応募フォームの説明にも書いているので、応募するときには説明をちゃんと読んで応募内容がずれていないか確認するとよいでしょう。

応募の書き方については以下の情報が参考になります。

明示的に書かれてい情報をインターネット上に見つけられませんでしたが、RubyKaigiは「Ruby」の「Kaigi」なので、Rubyで作られたなにかの発表よりもRubyそのものの発表の方がRubyKaigiにあっていそうです。

応募例:RubyKaigi 2015

RubyKaigi 2015での応募例です。この応募は採択されました。

発表内容:The history of testing framework in Ruby

Title:

The history of testing framework in Ruby

Abstract:

This talk describes about the history of testing framework in Ruby.

Ruby 2.2 bundles two testing frameworks. They are minitest and test-unit. Do you know why two testing frameworks are bundled? Do you know that test-unit was bundled and then removed from Ruby distribution? If you can't answer these questions and you're interested in testing framework, this talk will help you.

This talk describes about the history of bundled testing framework in Ruby and some major testing frameworks for Ruby in chronological order.

Details:

このトークは参加者が「自分にとって適切なテスティングフレームワークを選ぶための知識」を持ち帰れることを目標とします。

テスティングフレームワークはRubyで開発する上で重要な役割を持つソフトウェアです。Rubyを使うと非常に柔軟にプログラムを書くことができるため、意図せずに既存の挙動を変えてしまうことが起こしやすいです。自動化されたテストも一緒に開発すると、できるだけ低いコストでその問題の発生を抑えつつ、楽しくプログラムを書きやすくなります。それを支援するのがテスティングフレームワークです。

テスティングフレームワークはRubyで楽しくプログラムを書くために重要な役割にも関わらず、自分にとって適切なテスティングフレームワークを選ぶという活動はあまり行われていないように感じています。多くの人が使っているから、というのも選んだ理由としては妥当な理由ではあるのですが、流行が変わったとき・テスティングフレームワークのAPIが変わったときなど、今までと状況が変わったときに適切な対応を取りづらくなります。大きな流れが1つではなくなるからです。

自分で「どうしてこのテスティングフレームワークを選んでいるのか」を理解していれば、たとえ状況が変わったときでも適切な基準で適切なフレームワークを選びなおすことができるでしょう。

このトークではRubyにバンドルされたテスティングフレームワークの歴史といくつかの主要なRuby用のテスティングフレームワーク(RSpec, minitest, test-unitと関連ソフトウェアをいくつか)の歴史について時系列で紹介します。歴史を知ることで、それぞれのテスティングフレームワークが何を重視しているのか、そして、それが自分が大事にしていることと合致するのかを判断する材料になるはずです。これにより、参加者が「自分にとって適切なテスティングフレームワークを選ぶための知識」を持ち帰ることの実現を目指します。

Rubyにバンドルされたテスティングフレームワークの歴史については↓にまとめたものをベースとします。 http://www.clear-code.com/blog/2014/11/6.html

Pitch:

最新のRubyである2.2でtest-unitがバンドルされた背景(*)を知る人は少ないでしょう。RubyKaigiでのトークとして、RubyのユーザーがRubyの開発の歴史(の一部)を知る機会があるのは妥当だと考えます。

(*) Test::Unit互換APIを提供するため。そうしないと既存のテストが動かなくなってRubyのバージョンアップの障害になる人がでてバージョンアップの障害になるかもしれない。Python 3のように新しいバージョンがでても古いバージョンを使い続けるユーザーがたくさんいる状況は開発チームとしてはうれしくないので避けたい。

私はRubyのテスティングフレームワークの歴史に最初から関わっているわけではありません。Ruby 1.8と1.9の間くらいからだけです。しかし、その頃からRubyのテスティングフレームワークまわりについては観測してきましたし、このトークで登場するtest-unit gemテスティングフレームワークの現在のメンテナーです。そのため、私はこのトークをする人として適切だと考えます。

応募例:RubyKaigi 2016

RubyKaigi 2016での応募例です。この応募は採択されました。

発表内容:How to create bindings 2016

Title:

How to create bindings 2016

Abstract:

This talk describes how to create Ruby bindings of a C library. I want to increase Ruby bindings developers. If you're interested in using C libraries from Ruby and/or using Ruby more cases, this talk will help you.

This talk includes the following topics:

  • List of methods to create Ruby bindings of a C library.
  • Small example of each method.
  • Pros and cons of each method.

Details:

このトークの目標は参加者が「自分のユースケースにあったRubyバインディングの作り方を知ること」です。まったく知らないという参加者でもわかるようにします。「昔は知っていたけど最近のことはわからない」という参加者でも、2016年時点での最新情報を提供することで、得られるものがあるようにします。

多くのRubyistはRubyバインディングを作る機会はありませんし、作る必要もありません。しかし、作り方を知っていればいざというときにRubyバインディングを作るという選択肢が生まれ、よりRubyを活用できる可能性が高くなります。

Cライブラリーと連携できることはRubyのよいところの1つです。Cライブラリーと連携できると、Rubyスクリプトを高速化したり、既存のCライブラリーの機能を利用したりできます。速さが足りなくてRubyを使えない、機能が足りなくてRubyを使えない、そのようなときの解決策としてRubyバインディングの作り方が役に立ちます。

このトークではRubyバインディングの作り方として次の方法を紹介します。

  • RubyのC APIを使ったRubyバインディングの作り方
  • SWIGを使ったRubyバインディングの作り方
  • libffiを使ったRubyバインディングの作り方
  • GObject Introspectionを使ったRubyバインディングの作り方

それぞれについて具体的なコードとメリット・デメリットを紹介します。この情報をもって「自分のユースケースに適切なCライブラリーのRubyバインディングの作り方を知ること」の実現を目指します。

参考URL:スクリプト言語の拡張機能の作り方とGObject Introspectionの紹介

Pitch:

多くのRubyistはRubyバインディングを作る機会はありませんし、作る必要もありません。しかし、作り方を知っていればいざというときにRubyバインディングを作るという選択肢が生まれ、よりRubyを活用できる可能性が高くなります。

Cライブラリーと連携できることはRubyのよいところの1つです。Cライブラリーと連携できると、Rubyスクリプトを高速化したり、既存のCライブラリーの機能を利用したりできます。速さが足りなくてRubyを使えない、機能が足りなくてRubyを使えない、そのようなときの解決策としてRubyバインディングの作り方が役に立ちます。

以上より、Rubyを活用する機会を増やすため、RubyKaigiでRubyバインディングを作る方法を知る機会があるのは妥当だと考えます。

私は10個以上のRubyバインディングを作った経験があり、このトークで紹介するすべての作り方を使ってきました。そのため、私はこのトークをする人として適切だと考えます。

応募例:RubyKaigi 2017

RubyKaigi 2017での応募例です。この応募が採択されるかどうかはわかりません。

Title:

Improve extension API: C++ as better language for extension

Abstract:

This talk proposes better extension API.

The current extension API is C API. In the past, some languages such as Rust (RubyKaigi 2015), Go (Oedo RubyKaigi 05), rubex (RubyKaigi 2016) were proposed as languages to write extension.

This talks proposes C++ as a better language for writing extension. Reasons:

  • C++ API can provide simpler API than C API.
  • C++ API doesn't need C bindings because C++ can use C API including macro natively. Other languages such as Rust and Go need C bindings.
  • Less API maintenance cost. Other approaches need more works for Ruby evolution such as introduces new syntax and new API.

Details:

このトークの目標は参加者が「現在のC APIの課題を知ること」と「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」です。前者に関しては拡張ライブラリーを書いたことがない人でもわかるようにします。後者に関しては拡張ライブラリーを書いたことががない人には判断できないでしょう。よって、このトークのメインターゲットは拡張ライブラリーを書いたことがある人です。この人たちはこのトークでよりよい拡張ライブラリーのAPIについての知見を得るはずです。拡張ライブラリーを書いたことがない人たちは拡張ライブラリーのAPIそのものについての知見が増えているはずです。

このトークではC APIの課題として次のことを説明します。

  • 関数のシグネチャーを2回書かないといけず、それらがズレていてもコンパイル時に発見できないため、実行時にクラッシュしてしまう可能性が高まる。
  • 冗長である。たとえば、メソッドを登録するときにCの関数を定義してそれを別途登録するケース、eachbegin rescueを実現するためにCの関数を定義するケース、などである。
  • CのデータとRubyのデータ間の変換が面倒であり、入力チェックのコードが多くなってコードの見通しが悪くなり、メンテナンス性が下がる。
  • Rubyは例外が発生するとlongjumpするので、例外発生前に動的に確保したメモリーの開放処理が煩雑になる。

それぞれについて具体的なコードを紹介します。たとえば、関数のシグネチャーを2回書かないといけないとは次のようなコードのことです。

static VALUE
rb_sample_hello(VALUE self)
{
  return rb_str_new_static("Hello");
}

void
Init_sample(void)
{
  VALUE sample = rb_define_class("Sample", rb_cObject);
  rb_define_method(sample, "hello", rb_sample_hello, 0);
}

このコードではhelloメソッドの引数の数が0であることを示すために、rb_sample_hello(VALUE self)と関数を定義し、それをRubyに伝えるためにrb_define_method(..., 0)を実行しています。引数の数が増えた時は両方更新する必要があります。片方の更新を忘れるとクラッシュします。

このような情報をもって「現在のC APIの課題を知ること」の実現を目指します。

それぞれの課題について、課題を解決するC++ APIを提案します。APIだけでなくどうして解決できるのか、および、実装も説明します。このAPIとその解説および実装の説明により「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現を目指します。

たとえば、関数のシグネチャーを2回書かないといけないケースについては、次のようなC++ APIを提案します。

void
Init_sample(void)
{
  rb::Class("Sample", rb_cObject).
    define_method("hello", [](VALUE self) {return rb_str_new_static("Hello");});
}

このAPIではC++のラムダ式[](VALUE self)から引数が0であることがわかるため、その情報を自動的にRubyにも伝えます。これにより、シグネチャーの不整合によるクラッシュを防ぐことができます。[](const char *self)のようにシグネチャーが不正な時はコンパイル時にエラーにすることもできます。

このような情報をもって「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現を目指します。

また、RustやGoやrubexなど他のアプローチについても紹介し、それらのアプローチの課題も紹介します。

たとえば、RustやGoを使うアプローチではC APIのマクロの扱いに課題があります。これらの言語ではCのマクロを扱えないため、RSTRING_LEN()のようなC APIを使うためにはそれぞれの言語で同様の処理を実現する必要があります。rubexのアプローチではrubexという独自の言語の文法を覚える必要があるという学習コスト面と、rubex自体がRuby本体の進化(たとえば文法の追加)に追従しなければいけないというメンテナンス面の課題があります。

このような情報も「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現で役立つはずです。

もしかしたら、参考情報としてRubyのC++ APIを提供するRiceも紹介します。RiceはBoost.PythonのようなAPIを提供します。単なる拡張ライブラリーではなくバインディングを書くときにより便利です。「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現に役立つ情報なはずです。

Pitch:

Rubyは3.0で高速化を目指しています。Rubyが高速になることで、より速度を期待するユーザーが増えると予想します。Rubyレベルの高速化で満足できるユーザーも多いでしょうが、さらなる高速化を期待するユーザーも増えるはずです。

そうなったとき、より簡単に拡張ライブラリーを書けるようにすることでより簡単にRubyスクリプトを高速に実行できるようになります。そうなることで、そのようなユーザーも楽しくRubyを使えます。楽しくプログラムを書ける人が増えることはRubyのポリシーとあっています。

このトークで紹介する既存のアプローチは私のアイディアではありませんが、提案するC++ APIの部分は私のアイディアです。

私は10個以上の拡張ライブラリーを作った経験があり、C APIのよいところも課題も知っています。そのため、私はこのトークをする人として適切だと考えます。

まとめ

RubyKaigiの発表の応募例として私のここ3年の応募内容を紹介しました。RubyKaigi 2017の発表の応募は6月17日まで受け付けています。ぜひ応募してみてください。

タグ: Ruby
2017-06-06

関西Ruby会議2017:株式会社クリアコード #kanrk2017

須藤です。今年2回目の大阪です。(今年1回目の大阪はOSS Gate大阪ワークショップ2017-02-25でした。)

2017年5月27日に関西Ruby会議2017が開催されました。「Ruby Community and Ruby Business」がテーマということだったので、会社の話をするのがいいだろうなぁと思い、「株式会社クリアコード」というタイトルで基調講演をしました。

関連リンク:

内容

「Ruby Community and Ruby Business」というテーマなので、クリアコードは「Ruby Community」と「Ruby Business」を相互に活かしながらクリアコードが大事にしていることを実現している、という話をしました。クリアコードでの「Ruby Community」は「Ruby関連のフリーソフトウェア開発活動」、「Ruby Business」は「クリアコードでの仕事の仕方・作り方」です。クリアコードが大事にしていることは「フリーソフトウェアの推進と稼ぐことの両立」です。

1つのストーリーになった話ではなく、関連する話をたくさん集めた話にしました。具体的には次の話をしました。

  • 問題はupstreamで直す
  • 開発を続けられるコードを書く
  • 相手が想像しなくてもわかるように説明する
  • 楽しく開発する
  • 非難するよりも手を動かす
  • 回避策よりも根本解決
  • 受託開発
  • FLOSSサポート
  • OSS開発支援
  • 仕事の作り方:お客さん探しを頑張らない
  • Apache Arrow
  • 採用

最初の方の話は開発スタイルにまとまっている話です。

テーマに沿った内容になったと思っているのですが、いかがだったでしょうか?

まとめ

関西Ruby会議2017で基調講演をしてきました。「株式会社クリアコード」というタイトルでフリーソフトウェア開発と会社の話をしました。

この話を読んで仕事が決まる・採用の応募があるとすごく話がキレイにまとまるので、関西Ruby会議2017ではタイミングを逃してしまって話しかけられなかったという人は、遠慮せずにまだ間に合うのでご連絡ください。

タグ: Ruby | 会社
2017-05-29

DataScience.rb ワークショップ 〜ここまでできる Rubyでデータサイエンス〜:RubyもApache Arrowでデータ処理言語の仲間入り #datasciencerb

須藤です。最近気になるApache Arrowのissueは[ARROW-1055] [C++] Create add-on library for CUDA / GPU integrationです。

2017年5月19日に開催されたDataScience.rb ワークショップ 〜ここまでできる Rubyでデータサイエンス〜で「RubyもApache Arrowでデータ処理言語の仲間入り」という話をしました。このイベントはしまねソフト研究開発センタースポンサーのプロジェクトとRubyアソシエーションスポンサーのプロジェクトの成果発表会のような位置付けだと思うのですが、私がApache Arrowの紹介をしたいと言ったので、それらとまったく関係のないApache Arrowの話をねじ込んでもらいました。ありがとうございます。

関連リンク:

内容

Ruby界隈ではApache Arrowのことを知っている人はあまりいないはずです。その前提で、私の発表ではApache Arrowがなにを目指しているプロジェクトで、現状はどうなっていて、今後はどうなる予定か、ということを紹介しました。

詳細はスライドを参照してもらうとして、ざっくりいうと次のような感じです。

  • Apache Arrowが目指していること:
    • 低いデータ交換コスト
    • 最適化されたデータ処理実装の共有
  • Apache Arrowの現状:
    • 「低いデータ交換コスト」の方はプロダクションで使えそうなくらいになっている
    • 「最適化されたデータ処理実装の共有」の方はこれから
    • Java、C++、Python、Ruby、Lua、Go、JavaScript間で低コストでデータ交換できる
  • Apache Arrowの今後:
    • Apache Arrowを使ったデータ交換の推進(たとえば、SparkとPySpark間でのデータ交換にApache Arrowを使う変更がマージされて現実に使っていく。似たようなことをいろいろなプロジェクトで進めていく。)
    • 最適化されたデータ処理実装の提供
    • Apache Arrow対応言語の増加

どうしてRubyがApache Arrowに対応するとデータ処理できるようになるかを説明するために、まず、Apache Arrowがでてきた背景を説明します。

現在の(ビッグデータの)データ分析システムは1つの大きなソフトウェアでなんでもかんでも全部やるというやり方ではなく、複数のシステムが協調してデータ分析するというやり方になっています。たとえば、データの管理はHadoopでデータの分散処理基盤はSparkでユーザーがカスタマイズする部分はPythonで、といった具合です。

複数のシステムが協調するためには分析対象のデータを相互にやりとりする必要があります。データが大量にあるのでデータ交換のコストがどのくらいかは重要です。コストが高いと本来やりたかったデータ分析処理に時間を使えずにデータ交換ばかりに時間を使ってしまうからです。現状はコストが高くてそのような状況になりがちでした。それを解決するためにでてきたのがApache Arrowです。

Apache Arrowはデータのシリアライズ・デシリアライズコストをほぼ0にします。そのため、データ交換のコストの多くはデータの送受信くらいになります。別マシン間でのやりとりであればネットワーク上での通信処理、同一マシン間でのやり取りであればメモリー上へのデータの読み書き処理が主なコストになるので、かなり高速です。

データ交換コストが低くなると、データ分析システムの一部にRubyを使いやすくなります。これまでは、データ交換コストが高いので、できるだけサブシステム間でのデータ交換を減らしたくなりましたが、コストが低いとカジュアルにデータ交換できます。そうすると、「この処理はRubyが得意だからこの処理だけRubyでやろう」ということがしやすくなります。Rubyでできることが増えていくともっとRubyの活躍の場が増えます。

RubyがApache Arrowに対応するとRubyにデータが回ってくるようになるのでデータ処理できるようになるということです。

というのが私の目論見ですが、このようになるといいなぁと思ってRubyでApache Arrowを使えるようにする作業を進めています。現在の状況は次の通りです。

  • Apache ArrowはRubyを公式サポート
    • 私が開発した成果はApache Arrow本家に取り込まれました。継続的な活動を認められてコミッターにもなりました。
  • RubyからApache Parquet・Apache Arrow・Feather形式のデータを読み書き可能
    • Apache ParquetはHadoop界隈でよく使われている形式です。Hdoop方面のデータを読み書きできるということです。Apache ArrowのC++実装とApache ParquetのC++実装が連携しているので、それらの成果を利用しています。
    • FeatherはR界隈でよく使われている形式です。Rで処理したデータをRubyで読み込んだり、Rubyで収集・加工したデータをRに渡したりできます。
  • Apache Parquet・Apache Arrow・Feather形式のデータとRuby/GSL、NMatrix、Numo::NArrayのデータを相互変換可能
    • Ruby/GSL、NMatrix、Numo::NArrayはRuby用の行列演算ライブラリーです。読み込んだデータをこれらのライブラリーのオブジェクトに変換すればそれらの機能でデータを処理できます。
    • サンプルコードはkou/rabbit-slide-kou-data-science-rb/sample/にあります。
  • Apache Arrow形式のデータとGroongaのデータを相互変換可能
    • Groongaは集計機能も得意な全文検索ライブラリーです。RubyにはよくできたGroongaのバインディングがあるので、Groongaを使って高速にデータ処理することができます。たとえば、ログメッセージを全文検索して対象のログを絞り込んだり、全文検索インデックス内の統計情報を活用して重要語のみを抽出したりできます。
    • GroongaはApache Arrowに対応している(Groonga自体にApache Arrow形式のデータを読み書きする機能がある)ので、Rubyを経由せずに相互変換できて高速です。

今後ですが、1人でやっていては限界があるので、Red Data Toolsプロジェクトとして「Rubyでデータ処理したい!」という人たちと一緒に活動していく予定です。このプロジェクトは次のようなポリシーで活動していくので、賛同できる人はぜひ一緒にRubyでデータ処理できるようにしていきましょう!参加する人はチャットで参加表明してください。なにから着手するか相談しましょう。

  1. Rubyコミュニティーを超えて協力する
    • 私たちはRubyコミュニティーとも他のコミュニティーとも協力します。たとえば、私たちは多くの言語が共通で使っているApache Arrowを使いますし、Apache Arrowの開発に参加して開発成果を共有します。
  2. 非難することよりも手を動かすことが大事
    • 私たちは現状(RubyよりもPythonの方がたくさんよいツールが揃っているかもしれません)や既存ライブラリーの実装を非難することなどに時間を使うよりも、コードを書いたりテストをしたりドキュメントを書いたり私たちの活動を紹介したり他のプロジェクトにフィードバックをしたりといったことに時間を使います。
  3. 一回だけの活発な活動よりも小さくてもいいので継続的に活動することが大事
    • Rubyでたくさんのデータ処理をできるようになるために私たちはたくさんやることがあるかもしれません。データ処理のためのすばらしいツール群一式を揃えるために継続的に活動する必要があります。そのため、1回だけの活発な活動よりも継続的な活動の方が大事です。
  4. 現時点での知識不足は問題ではない
    • 私たちは高速なツールを実装するために数学や統計学や線形代数学などの知識が必要かもしれません。しかし、このプロジェクトに参加する時点でそれらの知識は必須ではありません。なぜなら活動をしていく中で学んでいくことができるからです。私たちは既存の高速な実装を使ったり、既存の高速な実装から学んだりすることができます。
  5. 部外者からの非難は気にしない
    • 私たちがデータ処理のためのすばらしいツール群一式を揃えるまでに時間がかかるかもしれません。そうなるまでは部外者が私たちの活動を非難するかもしれません。私たちはそれらを気にしません。私たちにはそれらを処理している時間はありません。 :-)
  6. 楽しくやろう!
    • Rubyを使っているんですから!

まとめ

Rubyでデータ処理できるようになるために役立ちそうなApache Arrowの紹介と現在できることを紹介しました。また、これからさらにRubyでデータ処理できる状況を推進していくために、Red Data Toolsプロジェクトを立ち上げました。Red Data Toolsプロジェクトに参加する人はチャットで参加表明してください。

「Rubyでデータ処理できるようにしよう!」活動を推進するために、開発費を提供するという方法もあります。手は動かせないけどお金は出せるという方はご連絡ください。

Apache Arrowは去年のはじめに始まったばかりの新しいプロジェクトなので、まだまだ知らない人も多いはずです。Apache Arrowが早く広まってデファクトスタンダードになると「Rubyでデータ処理」という文脈でもうれしいので、Rubyに関係あろうがなかろうが、Apache Arrowのイベントを開催したい場合はぜひお声がけください。日本でのApache Arrowの第一人者(?少なくともApache Arrowの日本人コミッター第1号)として協力できるはずです。

ちなみに、今度の日曜日(2017年5月28日)に大阪でApache Arrowオンリーイベントがあります。関西在住の方はぜひお越しください。現時点で定員オーバーですが、これからキャンセルが増えると思うので参加できるのではないかと思います。

つづき: 2017-06-12
タグ: Ruby
2017-05-22

名古屋Ruby会議03:Apache ArrowのRubyバインディングをGObject Introspectionで #nagoyark03

2017年2月11日に名古屋Ruby会議03大須演芸場で開催されました。ここで咳さんの並列処理啓蒙活動話の前座の1人として話してきました。内容は、Apache ArrowGObject IntrospectionRroongaを活用すれば自然言語のデータ分析の一部でRubyを活用できるよ!、です。

関連リンク:

リポジトリーには今回のスライド内で使ったスクリプトも入っています。

内容

当初は以下にいろいろまとめていた通り、Apache Arrow・GObject Introspectionはどんな特徴でどういう仕組みでそれを実現しているかといったことも説明するつもりでした。

ただ、内容をまとめていく過程で、特徴や仕組みの説明が多いと大須演芸場で話すには勢いが足りないと判断しました。その結果、詳細をもろもろ省略したストーリーベースの内容にしました。そのため、↑の内容はほぼ使っていません。

ストーリーベースの内容にしたことで「あぁ、たしかにデータ分析の一部でRubyを使えるかも」という雰囲気は伝わりやすくなったはず(どうだったでしょうか?)ですが、詳細の説明を省略したので詳細が気になったまま終わった人もいるかもしれません。そんな人たち向けに詳細がわかる追加情報を紹介します。

Apache ArrowとApache Parquet

まず、Apache ArrowとApache Parquetの連携についてです。両者はどちらもカラムストアのデータについて扱いますが、Apache Arrowはメモリー上での扱い、Aapache Parquetはストレージ上での扱いという違いがあります。この違いのためにトレードオフのバランスが変わっています。詳細はThe future of column-oriented data processing with Arrow and Parquetを見てください。2016年11月にニューヨークで開催されたDataEngConf NYCでのApache Parquetの作者の発表資料です。

ざっくりとまとめると次の通りです。

  • Apache Parquetはストレージ上でのカラムストアのデータの扱いなので、次の傾向がある。
    • CPUを活用するよりもI/Oを減らすほうが重要(たとえば、データを圧縮するとI/Oは減る一方CPU負荷は増えてしまうが、I/Oを減らしたいので圧縮をがんばる)
    • シーケンシャルアクセスが多い
  • Apache Arrowはメモリー上でのカラムストアのデータの扱いなので、次の傾向がある。
    • I/Oを減らすよりもCPUを活用するほうが重要(たとえば、CPUキャッシュミスが少なくなるようなデータ配置にする)
    • シーケンシャルアクセスもあるしランダムアクセスもある

データの配置や高速化の工夫なども前述の発表資料で説明しているので興味のある人は発表資料も確認してください。

Apache ArrowとApache Parquetは連携できます。具体的に言うと、Apache ParquetのC++実装にはApache Arrowのデータを読み書きできる機能があります。これを使うとApache ParquetのデータをApache Arrowのデータとして扱うことができます。

GObject Introspection

GObject Introspectionに関する情報は次の記事を参考にしてください。

GObject Introspectionに対応するとバインディングを書かなくてもCライブラリーのテストをRubyで書けるようになります。Cライブラリーの開発を捗らせるためにGObject Introspectionに対応させるというのもアリです。(これも当初は話したかったけど省略した話題です。)

GObject IntrospectionのRubyバインディングであるgobject-introspection gemについてはここで少し補足します。

話の中で、GI.loadした後、よりRubyっぽいAPIになるように一手間かけるとグッと使いやすくなると説明しました。当日は次のようにエイリアスを使う方法だけを紹介したのですが、別の方法もあってそれを紹介することをすっかり忘れていました。

require "gi"
Arrow = GI.load("Arrow")
class Arrow::Array
  def [](i)
    get_value(i)
  end
end

実はGObjectIntrospection::Loaderには定義するメソッド名を変える機能があります。上述のケースではエイリアスを作るのではなく、最初からget_value[]として定義するとよいです。

class Arrow::Loader < GObjectIntrospection::Loader
  private
  def rubyish_method_name(function_info, options={})
    # 引数が1つで、メソッド名がget_valueならメソッド名を[]にする
    if function_info.n_in_args == 1 and function_info.name == "get_value"
      "[]"
    else
      super
    end
  end
end

このようにGObjectIntrospection::Loaderをカスタマイズするやり方には次のメリットがあります。

  • 余計なメソッドを増やさない(今回のケースではget_value
  • 新しく同じパターンのメソッドが増えてもエイリアスを追加する必要がない(たとえば、Arrow::Array以外にget_value(i)なメソッドが増えてもバインディングを変更する必要がない)

この実装はRed Arrow(RArrowから名前を変更、由来はRubyは赤いからというのと西武新宿線の特急列車)にあります。open {|io| ...}を実現する方法も面白いので、GObject Introspectionが気になってきた方はぜひ実装も見てみてください。

まとめ

名古屋Ruby会議03で「Apache ArrowとGObject IntrospectionとRroongaを使って自然言語のデータ分析の一部でRubyを活用する」という話をしました。(使い方を間違っていましたが)はじめて小拍子を使ったり、はじめてマクラの後に羽織(?)を脱いだりできて、楽しかったです。貴重な経験になりました。声をかけてもらってありがとうございます。名古屋のみなさん(名古屋外からの参加の方も多かったですが)に楽しんでもらえていたなら、とてもうれしいことです。

今回の話では詳細をもろもろ省略しましたが、そのあたりに興味のある方がいたらぜひお声がけください。

また、クリアコードと一緒にRubyでデータ分析できる環境を整備していきたい!という方はぜひお問い合わせください。

タグ: Ruby
2017-02-15

名古屋Ruby会議03:Apache ArrowのRubyバインディング(6) #nagoyark03

前回はGObject Introspectionでのオブジェクトの寿命の扱いについて説明しました。今回は戻り値のオブジェクトの寿命について説明します。実際に動くバインディングはkou/arrow-glibにあります。

戻り値のオブジェクトの寿命

一般的に戻り値のオブジェクトの寿命には次のパターンがあります。

  • 所有権は関数実行側にあるパターン:戻り値のオブジェクトが必要なくなったら戻り値を受け取った側が責任を持って解放する。
  • 所有権は関数側にあるパターン:戻り値を受け取った側は解放してはいけない。

話を単純にするために、戻り値のオブジェクトとして文字列(char *)を考えてみましょう。

strdup(3)(引数の文字列をコピーして返す関数)は前者(所有権は関数実行側にあるパターン)です。strdup(3)の戻り値はstrdup(3)を呼び出した側が責任を持って解放しなければいけません。

char *copy;
copy = strdup("Hello");
free(copy); /* 呼び出し側がfree()する。 */

getenv(3)は後者(所有権は関数側にあるパターン)です。getenv(3)の戻り値はgetenv(3)を呼び出した側が解放してはいけません

char *path;
path = getenv("PATH");
/* free(path); */ /* 呼び出し側がfree()してはダメ! */

これが基本です。

ただし、前者の「所有権は関数実行側にあるパターン」にはもう少しパターンがあります。それは戻り値のオブジェクトがコンテナーの場合です。コンテナーというのは配列・リスト・ハッシュテーブルなどのように複数のオブジェクトを格納するオブジェクトのことです。

コンテナーの場合は、次のパターンがあります。

  • コンテナーの所有権のみ関数実行側にあるパターン
  • コンテナーの所有権もコンテナーの中のオブジェクトの所有権も関数実行側にあるパターン

コンテナーの所有権のみの場合はコンテナーのみを解放します。

GObject Introspectionでの戻り値のオブジェクトの寿命の指定

GObject Introspectionではこれらのそれぞれのパターンを指定できます。指定すると後はGObject Introspection(を使ったバインディング、Rubyならgobject-introspection gem)が適切に寿命を管理してくれます。

パターンはそれぞれの関数のドキュメントで指定します。

次の関数は新しくGArrowArrayオブジェクトを作って返す関数です。新しく作ったオブジェクトの所有権は関数呼び出し側にあります。そのため、「所有権は関数実行側にあるパターン」です。

/**
 * garrow_array_builder_finish:
 * @builder: A #GArrowArrayBuilder.
 *
 * Returns: (transfer full): The built #GArrowArray.
 */
GArrowArray *
garrow_array_builder_finish(GArrowArrayBuilder *builder)
{
  auto arrow_builder = garrow_array_builder_get_raw(builder);
  std::shared_ptr<arrow::Array> arrow_array;
  arrow_builder->Finish(&arrow_array);
  return garrow_array_new_raw(&arrow_array);
}

ポイントは次の箇所です。Returns:が戻り値に関する情報のドキュメントという意味で、(transfer full)が「所有権は呼び出し側にある」という意味です。

/**
 * Returns: (transfer full): The built #GArrowArray.
 */

「所有権は関数側にあるパターン」も見てみましょう。

次の関数はフィールド名を返す関数です。フィールド名はフィールドオブジェクトに所有権があるので関数呼び出し側は解放してはいけません。(gcharはGLibが提供しているchar型です。charと同じです。)

/**
 * garrow_field_get_name:
 * @field: A #GArrowField.
 *
 * Returns: The name of the field.
 */
const gchar *
garrow_field_get_name(GArrowField *field)
{
  const auto arrow_field = garrow_field_get_raw(field);
  return arrow_field->name.c_str();
}

ポイントは次の箇所です。戻り値の型にconstがついている場合はGObject Introspectionは所有権は関数側にあると推測します。そのため、明示的にReturns: (transfer none):と書く必要はありません。

const gchar *

「コンテナーの所有権のみ関数実行側にあるパターン」も見てみましょう。

arrow-glibでは使っていないのでGTK+にある関数を持ってきました。

/**
 * gtk_print_backend_load_modules:
 *
 * Returns: (element-type GtkPrintBackend) (transfer container):
 */
GList *
gtk_print_backend_load_modules (void)
{
  /* ... */
}

ポイントは(transfer container)です。これでコンテナーの所有権のみ関数呼び出し側にある、ということを指定します。

(element-type GtkPrintBackend)はコンテナー内の要素の型を指定しています。)

最後に「コンテナーの所有権もコンテナーの中のオブジェクトの所有権も関数実行側にあるパターン」を見てみましょう。

/**
 * garrow_record_batch_get_columns:
 * @record_batch: A #GArrowRecordBatch.
 *
 * Returns: (element-type GArrowArray) (transfer full):
 *   The columns in the record batch.
 */
GList *
garrow_record_batch_get_columns(GArrowRecordBatch *record_batch)
{
  const auto arrow_record_batch = garrow_record_batch_get_raw(record_batch);

  GList *columns = NULL;
  for (auto arrow_column : arrow_record_batch->columns()) {
    GArrowArray *column = garrow_array_new_raw(&arrow_column);
    columns = g_list_prepend(columns, column);
  }

  return g_list_reverse(columns);
}

ポイントは(transfer full)です。(transfer full)はコンテナーなオブジェクトにもそうでないオブジェクトにも使えるということです。

まとめ

GObject Introspectionでの戻り値のオブジェクトの寿命について説明しました。次回はインターフェイスについて説明します。

つづき: 2017-02-15
タグ: Ruby
2017-02-01

2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|
タグ: