Closed ring-c closed 6 months ago
Hey ring-c,
I updated the example to be more clear. It uses unix.BytePtrFromString
now.
You cannot use slices as struct fields. It has to be a pointer.
@ring-c Here is another example, which works for me:
.
├── go.mod
├── go.sum
├── hello.c
├── libhello.so
└── main.go
hello.c
#include <stdio.h>
typedef struct {
const char *question;
const char *answer;
} Dialog;
Dialog ask(Dialog in)
{
if (in.question != NULL)
{
puts(in.question);
}
Dialog out = { 0 };
out.question = in.question;
out.answer = "I am not your AI assistant!";
return out;
}
main.go
package main
import (
"fmt"
"unsafe"
"github.com/ebitengine/purego"
"github.com/jupiterrider/ffi"
"golang.org/x/sys/unix"
)
type Dialog struct {
Question, Answer *byte
}
func main() {
hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
if err != nil {
panic(err)
}
ask, err := purego.Dlsym(hello, "ask")
if err != nil {
panic(err)
}
var typDialog ffi.Type
typDialog.Type = ffi.Struct
typDialog.Elements = &[]*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, nil}[0]
var cif ffi.Cif
ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &typDialog, &typDialog)
var in, out Dialog
in.Question, _ = unix.BytePtrFromString("How will the weather be?")
ffi.Call(&cif, ask, unsafe.Pointer(&out), unsafe.Pointer(&in))
fmt.Println(unix.BytePtrToString(out.Answer))
}
[jupiterrider@pc42 demo]$ gcc -shared -o libhello.so -fPIC hello.c
[jupiterrider@pc42 demo]$ go run .
How will the weather be?
I am not your AI assistant!
[jupiterrider@pc42 demo]$
@JupiterRider thanks, that helped.
I did two major mistakes in my code: did not append nil
in my ffi.Type
def, and tried to call a function with accepting a pointer to a struct (as far as i understand C).
Is there a way to call such a function? Some thing like that:
void myFunk(myStruct* args)
If the c function wants a pointer, then you can just use the ffi.TypePointer
. Here is the the example:
#include <stdio.h>
typedef struct {
const char *question;
const char *answer;
} Dialog;
Dialog* ask(Dialog* in)
{
if (in->question != NULL)
{
puts(in->question);
}
Dialog out = { 0 };
out.question = in->question;
out.answer = "I am not your AI assistant!";
Dialog *r = &out;
return r;
}
package main
import (
"fmt"
"unsafe"
"github.com/ebitengine/purego"
"github.com/jupiterrider/ffi"
"golang.org/x/sys/unix"
)
type Dialog struct {
Question, Answer *byte
}
func main() {
hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
if err != nil {
panic(err)
}
ask, err := purego.Dlsym(hello, "ask")
if err != nil {
panic(err)
}
var cif ffi.Cif
ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &ffi.TypePointer, &ffi.TypePointer)
in := new(Dialog)
in.Question, _ = unix.BytePtrFromString("How will the weather be?")
var out *Dialog
ffi.Call(&cif, ask, unsafe.Pointer(&out), unsafe.Pointer(&in))
fmt.Println(unix.BytePtrToString(out.Answer))
}
You can also use plain purego:
package main
import (
"fmt"
"github.com/ebitengine/purego"
"golang.org/x/sys/unix"
)
type Dialog struct {
Question, Answer *byte
}
func main() {
hello, err := purego.Dlopen("./libhello.so", purego.RTLD_LAZY)
if err != nil {
panic(err)
}
var ask func(*Dialog) *Dialog
purego.RegisterLibFunc(&ask, hello, "ask")
in := new(Dialog)
in.Question, _ = unix.BytePtrFromString("How will the weather be?")
out := ask(in)
fmt.Println(unix.BytePtrToString(out.Answer))
}
purego can work with pointers to structs, but not with actual structs.
@JupiterRider thanks, this works.
As a continue of purego/issues/236
It does not work, even for struct with 1 string param. Getting
SIGSEGV: segmentation violation
In your example you are using pointer to result of
syscall.ByteSliceFromString
, so*[]byte
.