楼主:
Neisseria (Neisseria)
2016-10-24 09:57:33最近版工在把玩 Golang 这个新玩具
后来发现到可以用 Golang 写 Ruby gem,就上来分享一下
这个功能可以实现,主要有两点:
1. Go 在 1.5 版后可以输出给 C/C++ 使用的 shared library
2. Ruby 可透过 FFI 模组读取 shared library
其他的就是一些实作时的细节,请见下文。
首先,我们写一个简单的 Go 程式:
package main
import "C"
//export DoubleIt
func DoubleIt(x int) int {
return x * 2
}
func main() {}
然后,将其编译成 shared library:
$ go build -o libdoubler.so -buildmode=c-shared
会得到 libdoubler.h 和 libdoubler.so 这两个档案,可以后续给 C/C++ 使用
如果要给 Ruby 使用,需要用 FFI 这个模组读取 shared library,以下是范例:
require 'ffi'
module Lib
extend FFI::Library
ffi_lib 'c'
ffi_lib './libdoubler.so'
attach_function :DoubleIt, [:int], :int
end
puts Lib.DoubleIt(2)
接下来,我们要把这个过程写成 gem。
我们这里仅描述重点,其他的细节请自行参考这里:
https://github.com/cwchentw/doubler
传统的 Ruby extension gem,使用 C/C++ 或 Java,较少使用 Golang
所以,我们要自行撰写 extconf.rb,以便 gem 自动编译 shared library
以下是例子:
require 'mkmf' # For find_executable method
# You should check 1) Go environment exists and 2) Go version >= 1.5
# Check whether go command exists here
if find_executable('go').nil? then
abort <<NO_GO
No Go environment installed
NO_GO
end
# Implement code to check Go version
# We didn't check the version of Go here.
# In a formal gem, you should check that.
# Finally, call related go command to build shared library
system "go build -o libdoubler.so -buildmode=c-shared main.go"
本来,mkmf 模组为会我们制作 Makefile
然而,这个功能是给 C/C++ 用的,我们用不到
在这里,我们做一个没功能的 Makefile,以骗过 gem
all:
true
install:
true
最后,撰写相关的 gemspec 即可
Gem::Specification.new do |s|
s.name = "doubler"
s.version = "0.1.0"
s.licenses = ["MIT"]
s.summary = "A Demo Ruby Gem with Golang"
s.author = "Michael Chen"
s.email = "[email protected]"
s.extensions = %w[ext/doubler/extconf.rb]
files = Dir.glob("ext/**/*") + Dir.glob("lib/**/*.rb")
s.files = files
s.homepage = "https://github.com/cwchentw/doubler"
end
经实测,Golang 输出的函数,可以处理基本的数据,像是 int, float, string 等
但是,对于 array, map, struct 等进阶的资料类型则无法有效传递
以目前来说,大概就是把少数复杂的运算步骤,转给 Go 程式去运算
如果要在 C/C++ 层级处理复杂的资料类型和运算
一个就是回归 Ruby C API,但是这样就享受不到写 Golang 的便利
一个就是将资料转为 JSON 字串,在 Golang 和 Ruby 间传递
但是,这样效率可能不好,有兴趣的朋友可自行尝试
分享给有需要的 Rubyist