昨日の続き。
いよいよCインタフェースとの結びつけをやっていく。
グローバル変数の結びつけ
グローバル変数の結びつけを行うには、FFI::Library#attach_variableを使う。
そうすると、グローバル変数にアクセスするアクセサメソッドが使えるようになる。
attach_variable (グローバル変数名), (グローバル変数の型)
# もしくは
attach_variable (アクセサメソッド名), (グローバル変数名), (グローバル変数の型)
例えば、次のような感じ。
module MyLib extend FFI::Library ffi_lib 'c' # errnoの結びつけ attach_variable :errno, :int end # errnoを参照する puts MyLib.errno
関数の結びつけ
関数の結びつけを行うには、FFI::Library#attach_functionを使う。
そうすると、その関数をモジュール関数として使えるようになる。
attach_function (関数名), (引数の型の配列), (戻り値の型)
# もしくは
attach_function (モジュール関数名), (関数名), (引数の型の配列), (戻り値の型)
例えば、次のような感じ。
module MyLib extend FFI::Library ffi_lib 'c' # libcのputsをc_putsというモジュール関数名で結びつける attach_function :c_puts, :puts, [:string], :int end # libcのputsを呼び出す MyLib.c_puts("Hello, world!") #=> "Hello, world!"
voidの扱い
まず、引数をとらない関数の場合、引数の型の配列として空の配列を指定する。
例えば、次のようになる。
# int getchar(void);を結びつける attach_function :getchar, [], :int
そして、戻り値がない関数の場合、戻り値の型として:void
を指定する。
# void exit(int);を結びつける attach_function :exit, [:int], :void
構造体/共用体の扱い
関数に引数として構造体/共用体を渡す場合、値渡しと参照渡しの2通りの方法が存在する。
- 値渡しをする場合、引数の型としてFFI::Struct#by_valueを使う。(FFI::Struct#valも可)
- 参照渡しをする場合、引数の型としてFFI::Struct#by_refを使う。(FFI::Struct#ptrも可)
このように型を指定した場合、Ruby-FFIの方でうまいことやってくれるので、引数としてはどちらも構造体のオブジェクトを渡すだけでいい。
例えば、次のような感じ。
module MyLib extend FFI::Library ffi_lib 'mylib' class Point < FFI::Struct layout(:x, :double, :y, :double) end # 値渡しする関数、int set_point(Point p);を結びつける attach_function :set_point, [Point.by_value], :int # 参照渡しする関数、int get_point(Point *p);を結びつける attach_function :get_point, [Point.by_ref], :int end point = Point.new point[:x] = 1.0 point[:y] = 1.0 # 呼び出すときには値渡しか参照渡しか気にせず、単にオブジェクトを渡せばいい MyLib.set_point(point) MyLib.get_point(point)
なお、参照渡しをする場合、引数の型として:pointerをしてもいい。 ただし、この場合、呼び出すときにFFI::Struct#pointerメソッドでポインタを渡すようにしなければならない。
module MyLib ... # 引数の型を:pointerとして結びつける attach_function :get_point, [:pointer], :int end ... # 引数の型がポインタなので、自分でFFI::Struct#pointerを呼び出す必要がある MyLib.get_point(point.pointer)
関数の戻り値として構造体/共用体が返ってくる場合も、値そのものが返ってくる場合と、参照が返ってくる場合がある。
このときも引数の型の指定と同様に、by_value
なのかby_ref
なのかを指定する。
そうすれば、Ruby-FFIの方でうまいことやってくれるので、構造体のオブジェクトをそのまま受け取れる。
列挙型の扱い
関数の引数/戻り値の型が列挙型の場合、昨日書いた通りに列挙型を定義してあれば、引数/戻り値の型として列挙型を書いておくことで、Rubyのコードではシンボルだけを扱えばいいようになる。
例えば、次のような感じ。
module MyLib extend FFI::Library ffi_lib 'mylib' Type = enum( :triangle, 3, :rectangle, :pentagon) class Figure < FFI::Struct layout( :type, Type, :height, :uint, :width, :uint) end # オブジェクトを作成する関数 # Figure create_figure(Type type, unsigned int height, unsigned int width);を # 結びつける attach_function :create_figure, [Type, :uint, :uint], Figure.by_value # オブジェクトの形を返す関数 # Type get_figure_type(Figure *figure);を結びつける attach_function :get_figure_type, [Figure.by_ref], Type end # Ruby-FFIがうまいことやってくれるので、シンボルを扱えばいい figure = MyLib.create_figure(:triangle, 5, 10) p MyLib.get_figure_type(figure) #=> :triangle
今日はここまで!