3. Crystal C Library Bindings
lib LibC
fun pow(x: Float64, y: Float64) : Float64
fun sqrt(val: Float64) : Float64
end
puts(LibC.pow(10, 10))
puts(LibC.sqrt(4))
end
CrystalでCのバインディングを書くのはとても簡単
5. def fibonacci(n)
return n if n <= 1
fibonacci(n - 1) + fibonacci(n - 2)
end
まず、この単純なフィボナッチ数列の関数を
Ruby拡張ライブラリとしてCrystalで書きます
Example 1
6. lib LibRuby
type VALUE = Void*
$rb_cObject : VALUE
fun rb_define_global_function(name : UInt8*, func : VALUE, VALUE -> VALUE, argc : Int32)
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
end
def fibonacci_cr(self : LibRuby::VALUE, value : LibRuby::VALUE)
int_value = LibRuby.rb_num2int(value)
LibRuby.rb_int2inum(fibonacci_cr2(int_value))
end
def fibonacci_cr2(n)
return n if n <= 1
fibonacci_cr2(n - 1) + fibonacci_cr2(n - 2)
end
fun init = Init_extension_with_crystal
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
LibRuby.rb_define_global_function("fibonacci_cr", ->fibonacci_cr, 1);
end
Example 1 - fibobacci_cr
7. user system total real
fibonacci (ruby) 16.760000 0.040000 16.800000 ( 16.963310)
fibonacci (crystal) 0.830000 0.010000 0.840000 ( 0.827128)
うん、速い
Example 1
8. class Takeuchi
def self.tarai(x, y, z)
if y < x
tarai(
tarai(x - 1, y, z),
tarai(y - 1, z, x),
tarai(z - 1, x, y)
)
else
y
end
end
end
たらい回し関数も書いてみます
Example 2
9. lib LibRuby
type VALUE = Void*
$rb_cObject : VALUE
fun rb_define_class(name : UInt8*, super : VALUE) : VALUE
fun rb_define_module_function(klass : VALUE, name : UInt8*, func : VALUE, VALUE, VALUE, VALUE -> VALUE, argc : Int32)
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
end
def tarai(self : LibRuby::VALUE, x : LibRuby::VALUE, y : LibRuby::VALUE, z : LibRuby::VALUE)
int_x = LibRuby.rb_num2int(x)
int_y = LibRuby.rb_num2int(y)
int_z = LibRuby.rb_num2int(z)
LibRuby.rb_int2inum(tarai2(int_x, int_y, int_z))
end
def tarai2(x, y, z)
if y < x
tarai2(
tarai2(x - 1, y, z),
tarai2(y - 1, z, x),
tarai2(z - 1, x, y)
)
else
y
end
end
fun init = Init_extension_with_crystal
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
rb_class_takeuchi = LibRuby.rb_define_class("TakeuchiCr", LibRuby.rb_cObject)
LibRuby.rb_define_module_function(rb_class_takeuchi, "tarai", ->tarai, 3);
end
Example 2 - TakeuchiCr.tarai
10. user system total real
tarai (ruby) 19.810000 0.050000 19.860000 ( 20.248620)
tarai (crystal) 0.970000 0.000000 0.970000 ( 0.998764)
うん、速い
Example 2
14. Tsurami 3
fun rb_define_method(klass : VALUE,
name : UInt8*,
func : VALUE, VALUE -> VALUE,
argc : Int32)
• 関数の型を指定する必要がある
• これ以外の型の関数を渡すとコンパイルエラー
Ruby C API関数の引数が関数ポインタのとき…
15. crystal_ruby
Write Ruby extensions in Crystal.
https://github.com/manastech/crystal_ruby
• 公式による「Proof of Concept」
• 意識高い
• 最新のCrystalでは動かすのにちょっと修正が要る
16. Proof of Concept (PoC)
ruby_extension "test_ruby",
class Foo
def foo(a)
"From Crystal!! #{a}"
end
end
ruby_extensionマクロでコード(文字列)を処理
マクロに渡されたコードはParserでパースされる
17. Proof of Concept (PoC)
class MyVisitor < Visitor
…
def visit(node : ClassDef)
@str << %(_class = Ruby::Class.new "#{node.name}"n)
end
def visit(node : Def)
@str << %(_class.def "#{node.name}", #{node.args.size}, )
@str << %[->(self : LibRuby::VALUE, ]
node.args.each do |arg|
@str << %(_#{arg.name} : LibRuby::VALUE, )
…
end
…
end
ASTノードはVisitorを継承したクラスで処理される
関数ポインタは型をVoid*で定義しておいて、ここで具体的な型として設定される