Writing a Ruby extension in Go (golang)

后端 未结 3 1194
走了就别回头了
走了就别回头了 2021-01-30 00:12

Are there some tutorials or practical lessons on how to write an extension for Ruby in Go?

相关标签:
3条回答
  • 2021-01-30 00:40

    As of Go 1.5, there's a new build mode that tells the Go compiler to output a shared library and a C header file:

    -buildmode c-shared
    

    (This is explained in more detail in this helpful tutorial: http://blog.ralch.com/tutorial/golang-sharing-libraries/)

    With the new build mode, you no longer have to write a C glue layer yourself (as previously suggested in earlier responses). Once you have the shared-library and the header file, you can proceed to use FFI to call the Go-created shared library (example here: https://www.amberbit.com/blog/2014/6/12/calling-c-cpp-from-ruby/)

    0 讨论(0)
  • 2021-01-30 00:43

    Normally I'd try to give you a straight answer but the comments so far show there might not be one. So, hopefully this answer with a generic solution and some other possibilities will be acceptable.

    One generic solution: compile high level language program into library callable from C. Wrap that for Ruby. One has to be extremely careful about integration at this point. This trick was a nice kludge to integrate many languages in the past, usually for legacy reasons. Thing is, I'm not a Go developer and I don't know that you can compile Go into something callable from C. Moving on.

    Create two standalone programs: Ruby and Go program. In the programs, use a very efficient way of passing data back and forth. The extension will simply establish a connection to the Go program, send the data, wait for the result, and pass the result back into Ruby. The communication channel might be OS IPC, sockets, etc. Whatever each supports. The data format can be extremely simple if there's no security issues and you're using predefined message formats. That further boosts speed. Some of my older programs used XDR for binary format. These days, people seem to use things like JSON, Protocol Buffers and ZeroMQ style wire protocols.

    Variation of second suggestion: use ZeroMQ! Or something similar. ZeroMQ is fast, robust and has bindings for both languages. It manages the whole above paragraph for you. Drawbacks are that it's less flexible wrt performance tuning and has extra stuff you don't need.

    The tricky part of using two processes and passing data between them is a speed penalty. The overhead might not justify leaving Ruby. However, Go has great native performance and concurrency features that might justify coding part of an application in it versus a scripting language like Ruby. (Probably one of your justifications for your question.) So, try each of these strategies. If you get a working program that's also faster, use it. Otherwise, stick with Ruby.

    Maybe less appealing option: use something other than Go that has similar advantages, allows call from C, and can be integrated. Althought it's not very popular, Ada is a possibility. It's long been strong in native code, (restricted) concurrency, reliability, low-level support, cross-language development and IDE (GNAT). Also, Julia is a new language for high performance technical and parallel programming that can be compiled into a library callable from C. It has a JIT too. Maybe changing problem statement from Ruby+Go to Ruby+(more suitable language) will solve the problem?

    0 讨论(0)
  • 2021-01-30 00:53

    Go 1.5 added support for building shared libraries that are callable from C (and thus from Ruby via FFI). This makes the process easier than in pre-1.5 releases (when it was necessary to write the C glue layer), and the Go runtime is now usable, making this actually useful in real life (goroutines and memory allocations were not possible before, as they require the Go runtime, which was not useable if Go was not the main entry point).

    goFuncs.go:

    package main
    
    import "C"
    
    //export GoAdd
    func GoAdd(a, b C.int) C.int {
        return a + b
    }
    
    func main() {} // Required but ignored
    

    Note that the //export GoAdd comment is required for each exported function; the symbol after export is how the function will be exported.

    goFromRuby.rb:

    require 'ffi'
    
    module GoFuncs
      extend FFI::Library
      ffi_lib './goFuncs.so'
      attach_function :GoAdd, [:int, :int], :int
    end
    
    puts GoFuncs.GoAdd(41, 1)
    

    The library is built with:

    go build -buildmode=c-shared -o goFuncs.so goFuncs.go
    

    Running the Ruby script produces:

    42
    
    0 讨论(0)
提交回复
热议问题