2. About Me
● Masaki Matsushita
● CRuby Committer
○ 138 Commits
■ Mainly for performance improvement
■ Marshal.load, Hash#flatten, etc.
● Software Engineer at NTT Communications
○ Contribution to OpenStack
○ Slide at OpenStack Summit Tokyo
http://goo.gl/OXTYor
● Twitter: @_mmasaki Github: mmasaki
3. Today’s Topic
● Go 1.5 Feature: buildmode “c-shared”
○ Cgo Basics
● Using Go Function from Ruby
○ FFI and Fiddle without ruby.h
● Writing Extension Library with Go (and C)
○ Define Functions Equivalent to C Macros
○ Avoid Copy of Strings
○ Propagate Reference from Ruby to Go
○ Creating Gem including Go code
4. Buildmode “c-shared”
● Go 1.5 relased in August 2015
● Buildmode “c-shared” was introduced
○ go build -buildmode c-shared
○ Build C shared library with cgo
● cgo enables:
○ Refer to C functions, types and variables
○ Export Go functions for use by C
5. Cgo Example: Say hello with puts() in C
package main
/*
#include <stdlib.h>
#include <stdio.h>
*/
import "C"
import "unsafe"
func main() {
cstr := C.CString("Hello, world!")
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
}
Include C header file
Convert Go string into C String
6. Cgo Example: define and use C function
package main
/*
char *hello(void) {
return "Hello, world!";
}
*/
import "C"
import "fmt"
func main() {
cstr := C.hello()
fmt.Println(C.GoString(cstr))
}
Define C Function
Convert into Go String
Call C Function from Go
7. Try c-shared: add.go
package main
import "C"
//export add
func add(a C.int, b C.int) C.int {
return a + b
}
func main() {}
● go build -buildmode c-shared -o add.so add.go
Export Go Function for use by C
8. Load c-shared Libraries
● ruby-ffi
○ https://github.com/ffi/ffi
○ gem install ffi
● fiddle
○ Standard ext library
● useful to call Go functions simply
(without ruby.h)
9. Call Go Function from Ruby: ruby-ffi
require "ffi"
module Int
extend FFI::Library
ffi_lib "int.so"
attach_function :add, [:int, :int], :int
end
p Int.add(15, 27) #=> 42
Load c-shared library
Add Go Function to Module
10. Call Go Function from Ruby: fiddle
require "fiddle/import"
module Int
extend Fiddle::Importer
dlload "int.so"
extern "int add(int, int)"
end
p Int.add(15, 27) #=> 42
11. Go String and C String: str.so
package main
import "C"
import "fmt"
//export hello
func hello(cstr *C.char) {
str := C.GoString(cstr)
fmt.Println("Hello, " + str)
}
func main() {}
Receive C String
Convert to Go String
12. Returning String: ruby-ffi
require "ffi"
module Hello
extend FFI::Library
ffi_lib "str.so"
attach_function :hello, [:string], :void
end
Hello.hello("world") #=> "Hello, world"
Ruby String can be passed
14. Cgo Functions to Convert String
● C.CString(goString string) *C.char
○ copy Go String to C String
○ Users are responsible to free C String
● C.GoString(cString *C.char) string
● C.GoStringN(cString *C.char, length C.int) string
○ copy C String to Go String
15. Writing Extension Library with Go
● Naruse-san’s Amazing Talk:
“Writing extension libraries in Go”
at OedoRubyKaigi 05
https://speakerdeck.com/naruse/writing-extension-libraries-in-go
● gohttp: https://github.com/nurse/gohttp
○ Implementation of extension library in Go
21. Writing Extension Library with Go
● Wrapper Function equivalent to C Macro
○ C macros can’t be used by Cgo
● Convert Go String into Ruby without Copy
● Propagate Ruby Reference to Go
● Create gem including Go code
○ Modify Rakefile and extconf.rb
22. C Macros for Ruby Extention Libraries
● Useful C macros are defined in ruby.h
○ INT2NUM: C int to Ruby Numeric
○ NIL_P: true if obj is nil
○ RSTRING_PTR: pointer to buffer of String
○ RSTRING_LEN: lengh of String
● These macros can’t be used from Cgo…
● Define Go functions equivalent to C macros
○ Use equivalent C function
○ Wrap C macros with C function
23. Use Equivalent C Function
func LONG2NUM(n C.long) C.VALUE {
return C.rb_long2num_inline(n)
}
func NUM2LONG(n C.VALUE) C.long {
return C.rb_num2long(n)
}
24. Wrap C macros with C function
package main
/*
long rstring_len(VALUE str) {
return RSTRING_LEN(str);
}
*/
import "C"
func RSTRING_LEN(str C.VALUE) C.long {
return C.rstring_len(str)
}
25. Convert Go String into Ruby without Copy
● Go String -> C String -> Ruby String
● C.CString(goString string) *C.char
○ copy Go String to C String
○ Users are responsible to free C String
● VALUE rb_str_new(const char *ptr, long len)
○ copy C String to Ruby String
26. Basic Usage of C.CString()
// go/doc/progs/cgo4.go
func Print(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
}
● Call C func and discard C str soon
27. Basic Usage of C.CString()
str := "Hello, world!"
// Copy #1
cstr := C.CString(str) // will be discarded soon
// Copy #2
rbstr := C.rb_str_new(cstr, C.long(len(str)))
● Need to copy twice!
28. Avoid Copy of Strings
● Get *C.char from Go String without Copy
func GOSTRING_PTR(str string) *C.char {
bytes := *(*[]byte)(unsafe.Pointer(&str))
return (*C.char)(unsafe.Pointer(&bytes[0]))
}
// example of use
cstr := GOSTRING_PTR(str) C.rb_utf8_str_new
(cstr, C.long(len(str)))
29. Avoid Copy of Strings
● Technique to Get []byte from Go w/o Copy
http://qiita.com/mattn/items/176459728ff4f854b165
func GOSTRING_PTR(str string) *C.char {
bytes := *(*[]byte)(unsafe.Pointer(&str))
return (*C.char)(unsafe.Pointer(&bytes[0]))
}
30. Avoid Copy of Strings
● Get *C.char from []byte
func GOSTRING_PTR(str string) *C.char {
bytes := *(*[]byte)(unsafe.Pointer(&str))
return (*C.char)(unsafe.Pointer(&bytes[0]))
}
Cast to char
32. Propagate Ruby Reference to Go
● Go’s GC doesn’t know refs from Ruby
● Go obj referenced from Ruby can be collected
● We have to propagate Ruby Refs to Go
● Use Map to keep reference to Go Objects
36. Directory Structure
● Use “bundle gem --ext foo”
├── ext
│ └── foo
│ ├── extconf.rb // configured to use go build
│ ├── foo.c // helper functions for use by Go
│ └── foo.h // export helper functions
│ ├── foo.go // created by hand
│ └── wrapper.go // created by hand
└── lib
38. extconf.rb
require 'mkmf'
find_executable('go')
$objs = []
def $objs.empty?; false ;end
create_makefile("memberlist/memberlist")
case `#{CONFIG['CC']} --version`
when /Free Software Foundation/
ldflags = '-Wl,--unresolved-symbols=ignore-all'
when /clang/
ldflags = '-undefined dynamic_lookup'
end
● Some techniques to build successful
39. extconf.rb
File.open('Makefile', 'a') do |f|
f.write <<-EOS.gsub(/^ {8}/, "t")
$(DLLIB): Makefile $(srcdir)/memberlist.go $(srcdir)/wrapper.go
CGO_CFLAGS='$(INCFLAGS)' CGO_LDFLAGS='#{ldflags}'
go build -p 4 -buildmode=c-shared -o $(DLLIB)
EOS
end
● Modify Makefile to use go build
40. Ruby meets Go
● Buildmode “c-shared” and Cgo Basics
● Using Go Function from Ruby
○ FFI and Fiddle without ruby.h
● Writing Extension Library with Go
○ Define Functions Equivalent to C Macros
○ Avoid Copy of Strings
○ Propagate Reference from Ruby to Go
○ Creating Gem including Go code
● Let’s Hack Go for Ruby together!