2019年4月6日土曜日

ruby ffi 入門

概要

ffi は C や C++ ように作成された共有ライブラリを Ruby で扱うことができるようにするためのライブラリです
libffi を使っているので事前にインストールが必要です
NativeExtensions を直接扱わずに C を操作することができます
今回は簡単なサンプルコード動かしてみました

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57
    • ffi 1.10.0

インストール

libffi がない場合は

  • brew install libffi

でインストールできます

  • bundle init
  • vim Gemfile
gem "ffi"
  • bundle install --path vendor

Hello world

とりあえず Hello world してみます
libc の printf 関数を Ruby で扱えるようにしてみます

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib FFI::Library::LIBC
  attach_function("my_printf", "printf", [ :string, :string ], :int)
end

MyLib.my_printf("%s\n", "hello")

FFI::Library::LIBClibc.dylib という文字列が設定されています
各プラットフォームごとに読み込む共有ライブラリが違うので FFI::Library を使ったほうがいいです
Ruby 側で呼び出す関数名と C 側の関数名を紐付けるには attach_function を使います
「Ruby の関数名」「C の関数名」「引数の型」「返り値」で指定します

これで OK です
あとは module メソッドとしてコールするだけです

可変引数を取る場合

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib FFI::Library::LIBC
  attach_function("my_printf", "printf", [ :string, :varargs ], :int)
end

MyLib.my_printf("%s %d\n", :string, "hello", :int, 100)

:varargs を使います
型の指定が必要になりますがこれで複数の可変引数を取ることができます

指定可能なタイプの一覧

指定可能なタイプはここに一覧があります
もしくはプラットフォームごとに conf ファイルが用意されているのでそれで確認しても OK です

Pointer

Pointer も使えます

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib FFI::Library::LIBC
  attach_function("my_printf", "printf", [ :string, :pointer ], :int)
end

p1 = FFI::MemoryPointer.from_string('hello')
MyLib.my_printf("%p\n", p1)
p2 = FFI::MemoryPointer.new(:int, 5)
MyLib.my_printf("%p\n", p2)

Ruby 側でポインタ変数を扱う場合は FFI::MemoryPointer を使うと管理が楽になります

最後に

ruby-ffi を試してみました
libxxx 系の Ruby ラッパーを簡単に作ることができるようになるライブラリかなと思います
自分で so ファイルなどを作っている場合はこれを使うことで Ruby からコールすることも可能になります

参考サイト

0 件のコメント:

コメントを投稿