Answer the question
In order to leave comments, you need to log in
How to throw Callback in Go from under Ruby?
Go 1.5 introduced the ability to build c-shared libraries. On Habré there was an article on this topic, but there was a rather primitive example. That’s why it became interesting, is it possible to throw a callback function into Go from under Ruby in order to pull it as needed, well, or you never know for what else. I googled what is in FFI, what the documentation in CGO writes on the topic of external functions, and the following code came out:
package main
import (
"unsafe"
"fmt"
"time"
)
import "C"
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
//export asay
func asay(str string) {
go say(str)
}
//export ssay
func ssay(str string) {
say(str)
}
//export remote
func remote(pfoo unsafe.Pointer) {
foo := *(*())(pfoo)
foo()
}
func main() {}
require 'ffi'
#typedef struct { char *p; GoInt n; } GoString;
module Go
extend FFI::Library
ffi_lib 'libadd.so'
class String < FFI::Struct
layout :p, :pointer,
:n, :int
end
attach_function :asay, [Go::String.by_value], :void
attach_function :ssay, [Go::String.by_value], :void
callback :f_function, [], :void
attach_function :remote, [:f_function], :void
def self.say(text, async = false)
btext = text.dup.force_encoding 'binary'
str = Go::String.new
str[:p] = FFI::MemoryPointer.from_string(btext)
str[:n] = btext.size
if async
self.asay str
else
self.ssay str
end
end
def self._remote(&block)
func = FFI::Function.new(:void, []) { puts 'remote call' }
puts func.inspect
self.remote func
end
end
#<FFI::Function address=0x007f85006c8000 size=40>
unexpected fault address 0x0
fatal error: fault
[signal 0xb code=0x80 addr=0x0 pc=0x7f84fc1a2cd9]
goroutine 17 [running, locked to thread]:
runtime.throw(0x7f84fc2a8ac8, 0x5)
/var/user/.gvm/gos/go1.5/src/runtime/panic.go:527 +0x92 fp=0xc820038e88 sp=0xc820038e70
runtime.sigpanic()
/var/user/.gvm/gos/go1.5/src/runtime/sigpanic_unix.go:27 +0x2af fp=0xc820038ed8 sp=0xc820038e88
main.remote(0x7f85006c8000)
/var/user/www/goruby/lib.go:45 +0x19 fp=0xc820038ee0 sp=0xc820038ed8
runtime.call32(0x0, 0x7fff95f89198, 0x7fff95f89220, 0x8)
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:437 +0x40 fp=0xc820038f08 sp=0xc820038ee0
runtime.cgocallbackg1()
/var/user/.gvm/gos/go1.5/src/runtime/cgocall.go:252 +0x110 fp=0xc820038f40 sp=0xc820038f08
runtime.cgocallbackg()
/var/user/.gvm/gos/go1.5/src/runtime/cgocall.go:177 +0xd9 fp=0xc820038fa0 sp=0xc820038f40
runtime.cgocallback_gofunc(0x0, 0x0, 0x0)
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:801 +0x5d fp=0xc820038fb0 sp=0xc820038fa0
runtime.goexit()
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc820038fb8 sp=0xc820038fb0
goroutine 18 [syscall, locked to thread]:
runtime.goexit()
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:1696 +0x1
[1] 22080 abort ruby run.rb
Answer the question
In order to leave comments, you need to log in
and why to transfer lines in such a way? There is also a standard type :string.
I tried to write a simple parser in Go and pull it from the ruby code.
Github
As I understand it, ruby FFI passes a C string and it must be converted to the th string
//export grab
func grab(seedUrlsC *C.char) string {
seedUrlsString := C.GoString(seedUrlsC)
seedUrls := strings.Split(seedUrlsString, ",")
....
require 'ffi'
require 'json'
module ParserYandex
extend FFI::Library
ffi_lib 'lib/parserlib.so'
attach_function :grab, [:string], :string
end
def self.getData(urls)
stringUrls = urls.join ','
json_data = ParserYandex.grab(stringUrls)
data = JSON.parse(json_data)
data.each do |item|
puts item
end
end
urls = ["https://auto.yandex.ru/vaz/granta/7684152/complects"]
self.getData(urls)
I made the simplest binding in C:
typedef void (*r_callback)();
void remote_c(r_callback pfoo)
{
pfoo();
remote(pfoo);
}
void call() {
printf("c call\n");
}
void main() {
remote_c(call);
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question