OSSチャレンジPart6 (RubyのC API実装)
目的
学んだことを忘れないようにメモ!
間違えたことを書いている可能性あるので、注意してください!
あと、全部教えてもらいながらやったことを書いてるだけなので、自分の力ではないです。
やりたいこと
前回Part5で、作成したglibの実装を使って、MonthIntervalType等をred-arrowでも利用できるようになった。
しかし、arrowは大量のデータを使うので、一つ一つの値の変換処理を、Rubyでやっていたら遅いという問題がある。
なので、Cで変換するように他は実装されてるので、今回作ったMonthIntervalType等も同様にCで変換できるようにする。
やったこと
MonthIntervalType
たったこれだけ。
INT2NUMで、CのInt型から、Rubyで扱えるVALUE型(Rubyで扱うときはNumber型)に変換してる。
inline VALUE convert(const arrow::MonthIntervalArray& array, const int64_t i) { return INT2NUM(array.Value(i)); }
DayTimeIntervalType
DayTimeIntervalのvalueは、daysとmillisecondsというメンバー変数を持つDayMillisecondというクラス?なので、そのまま渡せない。
今回は、RubyのhashObjectを作って(rb_hash_new)、そこに rb_hash_aset(hash[0] = hoge みたいにhashの特定のkeyに値を入れられる)を入れて値を入れている。
変数のdaysとか、millisecondsは、int32なので、VALUE型(Rubyで扱うときはNumber型)に変換。
inline VALUE convert(const arrow::DayTimeIntervalArray& array, const int64_t i) { auto value = rb_hash_new(); auto arrow_value = array.Value(i); rb_hash_aset(value, red_arrow::symbols::day, INT2NUM(arrow_value.days)); rb_hash_aset(value, red_arrow::symbols::millisecond, INT2NUM(arrow_value.milliseconds)); return value; }
red_arrow::symbols::day は、以下のように定義してる。
rb_internとすると、整数ID型とやらにできる。
ID2SYMは整数ID型を受け取り、VALUE型(Rubyで扱うときはSymbol型)に変換してるから、ここでは、red_arrow::symbols::day
は「:day」のことを指す。
red_arrow::symbols::day = ID2SYM(rb_intern("day"));
テストリファクタ
こんな感じのクラスを追加すると、DayTimeIntervalArray.new
した時に、好きな形式(今回はHash)でvalueを渡すことができた!
module Arrow class DayTimeIntervalArrayBuilder private def convert_to_arrow_value(value) if value.is_a?(Hash) Arrow::DayMillisecond.new(value[:day], value[:millisecond]) else value end end end end
感想
Ruby はすべてのデータがオブジェクトって聞いたことがあって、よく分かってなかったけど、今回で理解できた。
Cから受け取った値をRubyで扱うために、全てValue型にしないといけないというのを実感できて、本当に勉強になった。
この実装方法を教えてもらってから、Ruby gemのC実装のコードもほぼ読めるようになったし、書けるようにもなってきたから、本当に感謝!!