2019年3月10日日曜日

RubyInline で配列 in 配列を扱う方法

概要

C 側で生成した多重配列を Ruby 側で使えるように変換するサンプルです

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57

サンプルコード

# coding: utf-8
require 'inline'

class MyTest
  inline do |builder|
    builder.add_compile_flags '-x c++', '-std=c++11'
    builder.include '<stdio.h>'
    builder.include '<malloc/malloc.h>'
    builder.c %q[
      VALUE array_in_array() {
        // ポインタを管理する配列
        uint8_t *ary[3];
        // 3 つポインタを作成し適当に値を設定
        uint8_t *buf1 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf1); i++) {
          buf1[i] = 'a';
        }
        ary[0] = buf1;
        uint8_t *buf2 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf2); i++) {
          buf2[i] = 'b';
        }
        ary[1] = buf2;
        uint8_t *buf3 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf3); i++) {
          buf3[i] = 'c';
        }
        ary[2] = buf3;
        // 設定したポイントを Ruby に返却する配列に変換する
        VALUE result = rb_ary_new2(3);
        for (int i = 0; i < 3; i++) {
          uint8_t *b = ary[i];
          VALUE r = rb_ary_new2(10);
          // 3 つポインタを変換し返却する配列に格納する
          for (int j = 0; j < malloc_size(b); j++) {
            rb_ary_store(r, j, rb_int_new(b[j]));
            printf("%c", b[j]);
          }
          rb_ary_store(result, i, r);
          printf("\n");
        }
        free(buf1);
        free(buf2);
        free(buf3);
        return result;
      }
    ]
  end
end

p = MyTest.new
result = p.array_in_array
result.each { |r|
  r.each { |buf|
    print buf.chr
  }
  puts
}

解説

今回作成される多重配列は以下のようになります

[
  ['a', 'a' ... 16個],
  ['b', 'b' ... 16個],
  ['c', 'c' ... 16個],
]

まず C 側でポインタを 3 つ生成しています
ポインタは 16 個の領域を持っています
それぞれに適当な char を設定していきます
設定した後に更にそれらのポインタを管理する配列 (ary) に格納します

C 側の多重配列の準備ができたらそれらを Ruby で使える多重配列に変換します
rb_ary_new2 で配列を作成し、作成した配列に対して rb_ary_store を使ってデータを格納していきます
あとは不要になった C 側のポインタ配列を開放します

実行してみるとわかりますが C 側で表示している情報と Ruby 側で表示している結果が同じになると思います

最後に

inline を使って多重配列を扱う方法を紹介しました
ruby.h の使い方に慣れていれば仕組み的にはそこまで難しいやり方ではないかなと思います

0 件のコメント:

コメントを投稿