x/ffi: wrap c/ffi

This commit is contained in:
visualfc
2024-10-23 11:44:34 +08:00
parent 92c267758e
commit 363be18599
15 changed files with 435 additions and 224 deletions

View File

@@ -29,29 +29,37 @@ type array struct {
k c.Int k c.Int
} }
var (
typeInt32 = &ffi.Type{4, 4, ffi.Sint32, nil}
typePointer = &ffi.Type{unsafe.Sizeof(0), uint16(unsafe.Alignof(0)), ffi.Pointer, nil}
)
func main() { func main() {
cdemo1() cdemo1()
cdemo2() cdemo2()
} }
func cdemo1() { func cdemo1() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.StructOf(ffi.TypeInt32, ffi.TypeInt32, ffi.TypeInt32, ffi.TypeInt32)) var cif ffi.Cif
if err != nil { tarray := &ffi.Type{0, 0, ffi.Struct, &[]*ffi.Type{typeInt32, typeInt32, typeInt32, typeInt32, nil}[0]}
panic(err) status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, typeInt32, &[]*ffi.Type{tarray}[0])
if status != ffi.OK {
panic(status)
} }
ar := array{1, 2, 3, 4} ar := array{1, 2, 3, 4}
var ret int32 var ret int32
ffi.Call(sig, c.Func(demo1), unsafe.Pointer(&ret), unsafe.Pointer(&ar)) ffi.Call(&cif, c.Func(demo1), unsafe.Pointer(&ret), &[]unsafe.Pointer{unsafe.Pointer(&ar)}[0])
c.Printf(c.Str("ret: %d\n"), ret) c.Printf(c.Str("ret: %d\n"), ret)
} }
func cdemo2() { func cdemo2() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer) var cif ffi.Cif
if err != nil { status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, typeInt32, &[]*ffi.Type{typePointer}[0])
panic(err) if status != ffi.OK {
panic(status)
} }
var ret int32 var ret int32
fn := c.Func(demo1) fn := c.Func(demo1)
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&fn)) ffi.Call(&cif, c.Func(demo2), unsafe.Pointer(&ret), &[]unsafe.Pointer{unsafe.Pointer(&fn)}[0])
c.Printf(c.Str("ret: %d\n"), ret) c.Printf(c.Str("ret: %d\n"), ret)
} }

View File

@@ -34,6 +34,11 @@ func demo(a array) c.Int {
return a.x + a.y + a.z + a.k return a.x + a.y + a.z + a.k
} }
var (
typeInt32 = &ffi.Type{4, 4, ffi.Sint32, nil}
typePointer = &ffi.Type{unsafe.Sizeof(0), uint16(unsafe.Alignof(0)), ffi.Pointer, nil}
)
func main() { func main() {
gofn() gofn()
c.Printf(c.Str("\n")) c.Printf(c.Str("\n"))
@@ -41,38 +46,48 @@ func main() {
} }
func gofn() { func gofn() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer) var cif ffi.Cif
if err != nil { status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, typeInt32, &[]*ffi.Type{typePointer}[0])
panic(err) if status != ffi.OK {
panic(status)
} }
closure := ffi.NewClosure() var fncode unsafe.Pointer
defer closure.Free() closure := ffi.ClosureAlloc(&fncode)
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) { defer ffi.ClosureFree(closure)
status = ffi.PreClosureLoc(closure, &cif, func(cif *ffi.Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
ar := *(*array)(ffi.Index(args, 0)) ar := *(*array)(ffi.Index(args, 0))
*(*c.Int)(ret) = demo(ar) *(*c.Int)(ret) = demo(ar)
}, nil) }, nil, fncode)
if status != ffi.OK {
panic(status)
}
var ret int32 var ret int32
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&closure.Fn)) ffi.Call(&cif, c.Func(demo2), unsafe.Pointer(&ret), &[]unsafe.Pointer{unsafe.Pointer(&fncode)}[0])
c.Printf(c.Str("ret: %d\n"), ret) c.Printf(c.Str("ret: %d\n"), ret)
} }
func goclosure() { func goclosure() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer) var cif ffi.Cif
if err != nil { status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, typeInt32, &[]*ffi.Type{typePointer}[0])
panic(err) if status != ffi.OK {
panic(status)
} }
fn := func(ar array) c.Int { fn := func(ar array) c.Int {
c.Printf(c.Str("call closure %d\n"), sig.NArgs) c.Printf(c.Str("call closure %d\n"), cif.NArgs)
return demo(ar) return demo(ar)
} }
closure := ffi.NewClosure() var fncode unsafe.Pointer
defer closure.Free() closure := ffi.ClosureAlloc(&fncode)
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) { defer ffi.ClosureFree(closure)
status = ffi.PreClosureLoc(closure, &cif, func(cif *ffi.Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
ar := *(*array)(ffi.Index(args, 0)) ar := *(*array)(ffi.Index(args, 0))
fn := *(*func(array) c.Int)(userdata) fn := *(*func(array) c.Int)(userdata)
*(*c.Int)(ret) = fn(ar) *(*c.Int)(ret) = fn(ar)
}, unsafe.Pointer(&fn)) }, unsafe.Pointer(&fn), fncode)
if status != ffi.OK {
panic(status)
}
var ret int32 var ret int32
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&closure.Fn)) ffi.Call(&cif, c.Func(demo2), unsafe.Pointer(&ret), &[]unsafe.Pointer{unsafe.Pointer(&fncode)}[0])
c.Printf(c.Str("ret: %d\n"), ret) c.Printf(c.Str("ret: %d\n"), ret)
} }

View File

@@ -7,14 +7,20 @@ import (
"github.com/goplus/llgo/c/ffi" "github.com/goplus/llgo/c/ffi"
) )
var (
typeInt32 = &ffi.Type{4, 4, ffi.Sint32, nil}
typePointer = &ffi.Type{unsafe.Sizeof(0), uint16(unsafe.Alignof(0)), ffi.Pointer, nil}
)
func main() { func main() {
sig, err := ffi.NewSignatureVar(ffi.TypeInt32, 1, ffi.TypePointer, ffi.TypeInt32) var cif ffi.Cif
if err != nil { status := ffi.PrepCifVar(&cif, ffi.DefaultAbi, 1, 2, typeInt32, &[]*ffi.Type{typePointer, typeInt32}[0])
panic(err) if status != ffi.OK {
panic(status)
} }
var ret int32 var ret int32
text := c.Str("hello world: %d\n") text := c.Str("hello world: %d\n")
var n int32 = 100 var n int32 = 100
ffi.Call(sig, c.Func(c.Printf), unsafe.Pointer(&ret), unsafe.Pointer(&text), unsafe.Pointer(&n)) ffi.Call(&cif, c.Func(c.Printf), unsafe.Pointer(&ret), &[]unsafe.Pointer{unsafe.Pointer(&text), unsafe.Pointer(&n)}[0])
c.Printf(c.Str("ret: %d\n"), ret) c.Printf(c.Str("ret: %d\n"), ret)
} }

View File

@@ -3,5 +3,5 @@
package ffi package ffi
const ( const (
DefaultAbi Abi = 1 DefaultAbi = 1
) )

View File

@@ -3,5 +3,5 @@
package ffi package ffi
const ( const (
DefaultAbi Abi = 2 DefaultAbi = 2
) )

View File

@@ -1,101 +1,127 @@
package ffi package ffi
import ( import (
"fmt"
"unsafe" "unsafe"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/ffi/ffi"
) )
type Type = ffi.Type const (
LLGoPackage = "link: $(pkg-config --libs libffi); -lffi"
type Signature = ffi.Cif LLGoFiles = "$(pkg-config --cflags libffi): _wrap/libffi.c"
)
type Abi c.Uint
type Error c.Uint
const ( const (
OK Error = iota Void = iota
Int
Float
Double
LongDouble
Uint8
Sint8
Uint16
Sint16
Uint32
Sint32
Uint64
Sint64
Struct
Pointer
Complex
)
const (
OK = iota
BAD_TYPEDEF BAD_TYPEDEF
BAD_ABI BAD_ABI
BAD_ARGTYPE BAD_ARGTYPE
) )
func (s Error) Error() string { type Type struct {
switch s { Size uintptr
case OK: Alignment uint16
return "ok" Type uint16
case BAD_TYPEDEF: Elements **Type
return "bad type def"
case BAD_ABI:
return "bad ABI"
case BAD_ARGTYPE:
return "bad argument type"
}
return fmt.Sprintf("invalid status: %v", int(s))
} }
func NewSignature(ret *Type, args ...*Type) (*Signature, error) { /*typedef struct {
var cif ffi.Cif ffi_abi abi;
var atype **Type unsigned nargs;
if len(args) > 0 { ffi_type **arg_types;
atype = &args[0] ffi_type *rtype;
} unsigned bytes;
status := ffi.PrepCif(&cif, c.Uint(DefaultAbi), c.Uint(len(args)), ret, atype) unsigned flags;
if status == 0 { #ifdef FFI_EXTRA_CIF_FIELDS
return &cif, nil FFI_EXTRA_CIF_FIELDS;
} #endif
return nil, Error(status) } ffi_cif;
*/
type Cif struct {
Abi c.Uint
NArgs c.Uint
ArgTypes **Type
RType *Type
Bytes c.Uint
Flags c.Uint
//Extra c.Uint
} }
func NewSignatureVar(ret *Type, fixed int, args ...*Type) (*Signature, error) { /*
var cif ffi.Cif ffi_status
var atype **Type ffi_prep_cif(ffi_cif *cif,
if len(args) > 0 { ffi_abi abi,
atype = &args[0] unsigned int nargs,
} ffi_type *rtype,
status := ffi.PrepCifVar(&cif, c.Uint(DefaultAbi), c.Uint(fixed), c.Uint(len(args)), ret, atype) ffi_type **atypes);
if status == 0 { */
return &cif, nil //go:linkname PrepCif C.ffi_prep_cif
} func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint
return nil, Error(status)
}
func Call(cif *Signature, fn unsafe.Pointer, ret unsafe.Pointer, args ...unsafe.Pointer) { /*
var avalues *unsafe.Pointer ffi_status ffi_prep_cif_var(ffi_cif *cif,
if len(args) > 0 { ffi_abi abi,
avalues = &args[0] unsigned int nfixedargs,
} unsigned int ntotalargs,
ffi.Call(cif, fn, ret, avalues) ffi_type *rtype,
} ffi_type **atypes);
*/
//go:linkname PrepCifVar C.ffi_prep_cif_var
func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint
type Closure struct { /*
ptr unsafe.Pointer void ffi_call(ffi_cif *cif,
Fn unsafe.Pointer void (*fn)(void),
} void *rvalue,
void **avalue);
*/
//go:linkname Call C.ffi_call
func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer)
func NewClosure() *Closure { // void *ffi_closure_alloc (size_t size, void **code);
c := &Closure{} //
c.ptr = ffi.ClosureAlloc(&c.Fn) //go:linkname ClosureAlloc C.llog_ffi_closure_alloc
return c func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer
}
func (c *Closure) Free() { // void ffi_closure_free (void *);
if c != nil && c.ptr != nil { //
ffi.ClosureFree(c.ptr) //go:linkname ClosureFree C.ffi_closure_free
c.ptr = nil func ClosureFree(unsafe.Pointer)
}
}
func (c *Closure) Bind(cif *Signature, fn ffi.ClosureFunc, userdata unsafe.Pointer) error { /*
status := ffi.PreClosureLoc(c.ptr, cif, fn, userdata, c.Fn) ffi_status
if status == 0 { ffi_prep_closure_loc (ffi_closure*,
return nil ffi_cif *,
} void (*fun)(ffi_cif*,void*,void**,void*),
return Error(status) void *user_data,
} void *codeloc);
*/
//llgo:type C
type ClosureFunc func(cif *Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer)
//go:linkname PreClosureLoc C.ffi_prep_closure_loc
func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint
func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer { func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(ptr) + offset) return unsafe.Pointer(uintptr(ptr) + offset)

View File

@@ -1,117 +0,0 @@
package ffi
import (
"unsafe"
"github.com/goplus/llgo/c"
)
const (
LLGoPackage = "link: $(pkg-config --libs libffi); -lffi"
LLGoFiles = "$(pkg-config --cflags libffi): _wrap/libffi.c"
)
const (
Void = iota
Int
Float
Double
LongDouble
Uint8
Sint8
Uint16
Sint16
Uint32
Sint32
Uint64
Sint64
Struct
Pointer
Complex
)
type Type struct {
Size uintptr
Alignment uint16
Type uint16
Elements **Type
}
/*typedef struct {
ffi_abi abi;
unsigned nargs;
ffi_type **arg_types;
ffi_type *rtype;
unsigned bytes;
unsigned flags;
#ifdef FFI_EXTRA_CIF_FIELDS
FFI_EXTRA_CIF_FIELDS;
#endif
} ffi_cif;
*/
type Cif struct {
Abi c.Uint
NArgs c.Uint
ArgTypes **Type
RType *Type
Bytes c.Uint
Flags c.Uint
//Extra c.Uint
}
/*
ffi_status
ffi_prep_cif(ffi_cif *cif,
ffi_abi abi,
unsigned int nargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCif C.ffi_prep_cif
func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
ffi_status ffi_prep_cif_var(ffi_cif *cif,
ffi_abi abi,
unsigned int nfixedargs,
unsigned int ntotalargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCifVar C.ffi_prep_cif_var
func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
void ffi_call(ffi_cif *cif,
void (*fn)(void),
void *rvalue,
void **avalue);
*/
//go:linkname Call C.ffi_call
func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer)
// void *ffi_closure_alloc (size_t size, void **code);
//
//go:linkname ClosureAlloc C.llog_ffi_closure_alloc
func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer
// void ffi_closure_free (void *);
//
//go:linkname ClosureFree C.ffi_closure_free
func ClosureFree(unsafe.Pointer)
/*
ffi_status
ffi_prep_closure_loc (ffi_closure*,
ffi_cif *,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data,
void *codeloc);
*/
//llgo:type C
type ClosureFunc func(cif *Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer)
//go:linkname PreClosureLoc C.ffi_prep_closure_loc
func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint

25
x/ffi/_demo/_wrap/wrap.c Normal file
View File

@@ -0,0 +1,25 @@
#include <stdio.h>
struct array
{
int x;
int y;
int z;
int k;
};
int demo1(struct array a)
{
printf("c.demo1: %d %d %d %d\n",a.x,a.y,a.z,a.k);
return a.x+a.y+a.z+a.k;
}
int demo2( int (*fn)(struct array)) {
printf("c.demo2: %p\n",fn);
struct array a;
a.x = 1;
a.y = 2;
a.z = 3;
a.k = 4;
return (*fn)(a);
}

57
x/ffi/_demo/cfunc/main.go Normal file
View File

@@ -0,0 +1,57 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/x/ffi"
)
const (
LLGoPackage = "link"
LLGoFiles = "../_wrap/wrap.c"
)
//llgo:type C
type Callback func(array) c.Int
//go:linkname demo1 C.demo1
func demo1(array) c.Int
//go:linkname demo2 C.demo2
func demo2(fn Callback) c.Int
//llgo:type C
type array struct {
x c.Int
y c.Int
z c.Int
k c.Int
}
func main() {
cdemo1()
cdemo2()
}
func cdemo1() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.StructOf(ffi.TypeInt32, ffi.TypeInt32, ffi.TypeInt32, ffi.TypeInt32))
if err != nil {
panic(err)
}
ar := array{1, 2, 3, 4}
var ret int32
ffi.Call(sig, c.Func(demo1), unsafe.Pointer(&ret), unsafe.Pointer(&ar))
c.Printf(c.Str("ret: %d\n"), ret)
}
func cdemo2() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer)
if err != nil {
panic(err)
}
var ret int32
fn := c.Func(demo1)
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&fn))
c.Printf(c.Str("ret: %d\n"), ret)
}

View File

@@ -0,0 +1,78 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/x/ffi"
)
const (
LLGoPackage = "link"
LLGoFiles = "../_wrap/wrap.c"
)
//llgo:type C
type Callback func(array) c.Int
//go:linkname demo1 C.demo1
func demo1(array) c.Int
//go:linkname demo2 C.demo2
func demo2(fn Callback) c.Int
//llgo:type C
type array struct {
x c.Int
y c.Int
z c.Int
k c.Int
}
func demo(a array) c.Int {
c.Printf(c.Str("go.demo %d %d %d %d\n"), a.x, a.y, a.z, a.k)
return a.x + a.y + a.z + a.k
}
func main() {
gofn()
c.Printf(c.Str("\n"))
goclosure()
}
func gofn() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer)
if err != nil {
panic(err)
}
closure := ffi.NewClosure()
defer closure.Free()
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
ar := *(*array)(ffi.Index(args, 0))
*(*c.Int)(ret) = demo(ar)
}, nil)
var ret int32
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&closure.Fn))
c.Printf(c.Str("ret: %d\n"), ret)
}
func goclosure() {
sig, err := ffi.NewSignature(ffi.TypeInt32, ffi.TypePointer)
if err != nil {
panic(err)
}
fn := func(ar array) c.Int {
c.Printf(c.Str("call closure %d\n"), sig.NArgs)
return demo(ar)
}
closure := ffi.NewClosure()
defer closure.Free()
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
ar := *(*array)(ffi.Index(args, 0))
fn := *(*func(array) c.Int)(userdata)
*(*c.Int)(ret) = fn(ar)
}, unsafe.Pointer(&fn))
var ret int32
ffi.Call(sig, c.Func(demo2), unsafe.Pointer(&ret), unsafe.Pointer(&closure.Fn))
c.Printf(c.Str("ret: %d\n"), ret)
}

View File

@@ -3,7 +3,7 @@ package main
import ( import (
"unsafe" "unsafe"
"github.com/goplus/llgo/c/ffi" "github.com/goplus/llgo/x/ffi"
) )
type array struct { type array struct {

View File

@@ -0,0 +1,20 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/x/ffi"
)
func main() {
sig, err := ffi.NewSignatureVar(ffi.TypeInt32, 1, ffi.TypePointer, ffi.TypeInt32)
if err != nil {
panic(err)
}
var ret int32
text := c.Str("hello world: %d\n")
var n int32 = 100
ffi.Call(sig, c.Func(c.Printf), unsafe.Pointer(&ret), unsafe.Pointer(&text), unsafe.Pointer(&n))
c.Printf(c.Str("ret: %d\n"), ret)
}

93
x/ffi/ffi.go Normal file
View File

@@ -0,0 +1,93 @@
package ffi
import (
"fmt"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/ffi"
)
type Type = ffi.Type
type Signature = ffi.Cif
type Error int
func (s Error) Error() string {
switch s {
case ffi.OK:
return "ok"
case ffi.BAD_TYPEDEF:
return "bad type def"
case ffi.BAD_ABI:
return "bad ABI"
case ffi.BAD_ARGTYPE:
return "bad argument type"
}
return fmt.Sprintf("invalid status: %v", int(s))
}
func NewSignature(ret *Type, args ...*Type) (*Signature, error) {
var cif Signature
var atype **Type
if len(args) > 0 {
atype = &args[0]
}
status := ffi.PrepCif(&cif, ffi.DefaultAbi, c.Uint(len(args)), ret, atype)
if status == 0 {
return &cif, nil
}
return nil, Error(status)
}
func NewSignatureVar(ret *Type, fixed int, args ...*Type) (*Signature, error) {
var cif Signature
var atype **Type
if len(args) > 0 {
atype = &args[0]
}
status := ffi.PrepCifVar(&cif, ffi.DefaultAbi, c.Uint(fixed), c.Uint(len(args)), ret, atype)
if status == ffi.OK {
return &cif, nil
}
return nil, Error(status)
}
func Call(cif *Signature, fn unsafe.Pointer, ret unsafe.Pointer, args ...unsafe.Pointer) {
var avalues *unsafe.Pointer
if len(args) > 0 {
avalues = &args[0]
}
ffi.Call(cif, fn, ret, avalues)
}
type Closure struct {
ptr unsafe.Pointer
Fn unsafe.Pointer
}
func NewClosure() *Closure {
c := &Closure{}
c.ptr = ffi.ClosureAlloc(&c.Fn)
return c
}
func (c *Closure) Free() {
if c != nil && c.ptr != nil {
ffi.ClosureFree(c.ptr)
c.ptr = nil
}
}
func (c *Closure) Bind(cif *Signature, fn ffi.ClosureFunc, userdata unsafe.Pointer) error {
status := ffi.PreClosureLoc(c.ptr, cif, fn, userdata, c.Fn)
if status == ffi.OK {
return nil
}
return Error(status)
}
func Index(args *unsafe.Pointer, i uintptr) unsafe.Pointer {
return ffi.Index(args, i)
}

View File

@@ -4,7 +4,7 @@ import (
"unsafe" "unsafe"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/ffi/ffi" "github.com/goplus/llgo/c/ffi"
) )
type BasicKind int type BasicKind int