2019年3月17日日曜日

複数の .c ファイルを作成して NativeExtensions を開発する方法

概要

NativeExtensions でクラスやモジュールを複数定義した場合があると思います
構造体も異なる場合は別の .c ファイルを作成したほうが良い場合があると思います
今回はその方法を紹介します

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57

.c ファイルを追加する

まずは別のクラスを管理する C のコードを追加しましょう

  • touch ext/firstext/secondext.c
  • vim ext/firstext/secondext.c
#include <ruby.h>

void Init_secondext(void) {
  VALUE se = rb_define_class("SecondExt", rb_cObject);
}

とりあえずクラスだけ定義しています
中身は空です

エントリーポイントとなる .c ファイルから参照する

NativeExtensions は Makefile 内で TARGET_ENTRY を指定しているのですが 1 つしか指定しません
なので、そのエントリーポイントから追加した secondext.c を参照します

  • vim ext/firstext/firstext.c
#... ↑ 省略

void Init_firstext(void) {
  VALUE fe = rb_define_class("FirstExt", rb_cObject);
  rb_define_alloc_func(fe, my_struct_alloc);
  rb_define_method(fe, "set", wrap_set, 0);
  rb_define_method(fe, "show", wrap_show, 0);
  Init_secondext();
}

最後の Init_secondext(); を 1 行追加しただけです
今回はヘッダファイル (.h) は作成しておらずソースファイル (.c) だけです
同一階層にソースファイルを追加した場合は include しなくても別ソースファイルの関数を参照できました

コンパイル&テスト

  • bundle exec rake clean
  • bundle exec rake compile

でいつも通り lib/firstext.bundle が作成されます
ちゃんと SecondExt が使えるかテストしましょう

RSpec.describe FirstExt do
  it "has a version number" do
    expect(FirstExt::VERSION).not_to be nil
  end

  it "create a instance" do
    cli = FirstExt.new
    cli.set
    cli.show
    cli = SecondExt.new
  end
end
  • bundle exec rake spec

でちゃんと SecondExt クラスが new できるようになっていると思います
クラスが見つからないというエラーが出る場合は再度コンパイルし直してみてください

最後に

NativeExtensions の開発時に複数のソースファイルに分けて開発する際のポイントを紹介しました
基本的にはクラスやモジュール単位でソースファイルも分かると良いかなと思います

今回はヘッダファイルを作成していません
お作法的にはちゃんとヘッダファイルを書くべきです
他に気になるワードとしては depend という機能がありこれに依存関係を書いておくと依存関係のあるファイルを自動でコンパイルしてくれます
rake-compiler を使っている場合はもしかしたら不要っぽかったので今回は使っていません

参考サイト

0 件のコメント:

コメントを投稿