Commits

Sebastien Binet  committed 2bb1c31

fixups + tests for math

  • Participants
  • Parent commits 8b7d3cf

Comments (0)

Files changed (2)

File pkg/ffi/ffi.go

 type Abi C.ffi_abi
 
 const (
-	FirstAbi Abi   = C.FFI_FIRST_ABI
+	FirstAbi   Abi = C.FFI_FIRST_ABI
 	DefaultAbi Abi = C.FFI_DEFAULT_ABI
-	LastAbi Abi = C.FFI_LAST_ABI
+	LastAbi    Abi = C.FFI_LAST_ABI
 )
 
 const (
 	TrampolineSize = C.FFI_TRAMPOLINE_SIZE
 	NativeRawApi   = C.FFI_NATIVE_RAW_API
 
-	Closures          = C.FFI_CLOSURES
-	TypeSmallStruct1B = C.FFI_TYPE_SMALL_STRUCT_1B
-	TypeSmallStruct2B = C.FFI_TYPE_SMALL_STRUCT_2B
-	TypeSmallStruct4B = C.FFI_TYPE_SMALL_STRUCT_4B
+	//Closures          = C.FFI_CLOSURES
+	//TypeSmallStruct1B = C.FFI_TYPE_SMALL_STRUCT_1B
+	//TypeSmallStruct2B = C.FFI_TYPE_SMALL_STRUCT_2B
+	//TypeSmallStruct4B = C.FFI_TYPE_SMALL_STRUCT_4B
 )
 
-// 
+// Type is a FFI type, describing functions' type arguments
 type Type struct {
-	c C.ffi_type
+	c *C.ffi_type
 }
 
 var (
-	Void       = Type{C.ffi_type_void}
-	Uchar      = Type{C.ffi_type_uchar}
-	Schar      = Type{C.ffi_type_schar}
-	Ushort     = Type{C.ffi_type_ushort}
-	Sshort     = Type{C.ffi_type_sshort}
-	Uint       = Type{C.ffi_type_uint}
-	Sint       = Type{C.ffi_type_sint}
-	Ulong      = Type{C.ffi_type_ulong}
-	Slong      = Type{C.ffi_type_slong}
-	Uint8      = Type{C.ffi_type_uint8}
-	Sint8      = Type{C.ffi_type_sint8}
-	Uint16     = Type{C.ffi_type_uint16}
-	Sint16     = Type{C.ffi_type_sint16}
-	Uint32     = Type{C.ffi_type_uint32}
-	Sint32     = Type{C.ffi_type_sint32}
-	Uint64     = Type{C.ffi_type_uint64}
-	Sint64     = Type{C.ffi_type_sint64}
-	Float      = Type{C.ffi_type_float}
-	Double     = Type{C.ffi_type_double}
-	LongDouble = Type{C.ffi_type_longdouble}
-	Pointer    = Type{C.ffi_type_pointer}
+	Void       = Type{&C.ffi_type_void}
+	Uchar      = Type{&C.ffi_type_uchar}
+	Schar      = Type{&C.ffi_type_schar}
+	Ushort     = Type{&C.ffi_type_ushort}
+	Sshort     = Type{&C.ffi_type_sshort}
+	Uint       = Type{&C.ffi_type_uint}
+	Sint       = Type{&C.ffi_type_sint}
+	Ulong      = Type{&C.ffi_type_ulong}
+	Slong      = Type{&C.ffi_type_slong}
+	Uint8      = Type{&C.ffi_type_uint8}
+	Sint8      = Type{&C.ffi_type_sint8}
+	Uint16     = Type{&C.ffi_type_uint16}
+	Sint16     = Type{&C.ffi_type_sint16}
+	Uint32     = Type{&C.ffi_type_uint32}
+	Sint32     = Type{&C.ffi_type_sint32}
+	Uint64     = Type{&C.ffi_type_uint64}
+	Sint64     = Type{&C.ffi_type_sint64}
+	Float      = Type{&C.ffi_type_float}
+	Double     = Type{&C.ffi_type_double}
+	LongDouble = Type{&C.ffi_type_longdouble}
+	Pointer    = Type{&C.ffi_type_pointer}
 )
 
 type Status uint32
 	panic("unreachable")
 }
 
-// Arg is a ffi argument
-type Arg struct {
-	c C.ffi_arg
-}
+// // Arg is a ffi argument
+// type Arg struct {
+// 	c C.ffi_arg
+// }
 
-// SArg is a ffi argument
-type SArg struct {
-	c C.ffi_sarg
-}
+// // SArg is a ffi argument
+// type SArg struct {
+// 	c C.ffi_sarg
+// }
 
 // Cif is the ffi call interface
 type Cif struct {
 }
 
 type FctPtr struct {
-	//c unsafe.Pointer
 	c C._go_ffi_fctptr_t
 }
 
 	var c_args **C.ffi_type = nil
 	if len(args) > 0 {
 		var cargs = make([]*C.ffi_type, len(args))
-		for i,_ := range args {
-			cargs[i] = &args[i].c
+		for i, _ := range args {
+			cargs[i] = args[i].c
 		}
 		c_args = &cargs[0]
 	}
-	sc := C.ffi_prep_cif(&cif.c, C.ffi_abi(abi), c_nargs, &rtype.c, c_args)
+	sc := C.ffi_prep_cif(&cif.c, C.ffi_abi(abi), c_nargs, rtype.c, c_args)
 	if sc != C.FFI_OK {
-		return nil, fmt.Errorf("error while preparing cif (%s)", 
+		return nil, fmt.Errorf("error while preparing cif (%s)",
 			Status(sc))
 	}
 	return cif, nil
 }
 
 // Call invokes the cif with the provided function pointer and arguments
-func (cif *Cif) Call(fct FctPtr, args ...interface{}) (interface{}, error) {
+func (cif *Cif) Call(fct FctPtr, args ...interface{}) (reflect.Value, error) {
 	nargs := len(args)
 	if nargs != int(cif.c.nargs) {
-		return nil, fmt.Errorf("ffi: invalid number of arguments. expected '%d', got '%s'.",
+		return reflect.New(reflect.TypeOf(0)), fmt.Errorf("ffi: invalid number of arguments. expected '%d', got '%s'.",
 			int(cif.c.nargs), nargs)
 	}
 	var c_args *unsafe.Pointer = nil
 	if nargs > 0 {
 		cargs := make([]unsafe.Pointer, nargs)
-		for i,_ := range args {
+		for i, _ := range args {
+			//fmt.Printf("[%d]: (%v)\n", i, args[i])
 			cargs[i] = to_voidptr(args[i])
 		}
 		c_args = &cargs[0]
 	}
-	var out interface{} = nil
-	var c_out unsafe.Pointer = nil
+	out := reflect.New(rtype_from_ffi(cif.c.rtype))
+	var c_out unsafe.Pointer = unsafe.Pointer(out.Elem().UnsafeAddr())
+	//println("...ffi_call...")
 	C.ffi_call(&cif.c, fct.c, c_out, c_args)
-	return out, nil
+	//fmt.Printf("...ffi_call...[done] [%v]\n",out.Elem())
+	return out.Elem(), nil
+}
+
+func rtype_from_ffi(t *C.ffi_type) reflect.Type {
+	switch t {
+	case &C.ffi_type_uint:
+		return reflect.TypeOf(uint(0))
+	case &C.ffi_type_sint:
+		return reflect.TypeOf(int(0))
+	case &C.ffi_type_float:
+		return reflect.TypeOf(float32(0))
+	case &C.ffi_type_double:
+		return reflect.TypeOf(float64(0))
+	}
+	panic("unreachable")
 }
 
 // void ffi_call(ffi_cif *cif,
 	c C.ffi_closure
 }
 
-
 // utils ---
 
 // to_voidptr wraps an interface value into an unsafe.Pointer
 func to_voidptr(v interface{}) unsafe.Pointer {
+	t := reflect.TypeOf(v)
+	rv := reflect.ValueOf(v)
+	//println("-->",t.Kind().String())
+	switch t.Kind() {
+	case reflect.String:
+		//fixme: memleak
+		cstr := C.CString(rv.String())
+		return unsafe.Pointer(&cstr)
+	case reflect.Ptr:
+		return unsafe.Pointer(rv.Elem().UnsafeAddr())
+	case reflect.Float64:
+		vv := rv.Float()
+		rv = reflect.ValueOf(&vv)
+		return unsafe.Pointer(rv.Elem().UnsafeAddr())
+	}
+	panic("to-voidptr: unreachable [" + t.Kind().String() + "]")
 	return nil
 }
 
 }
 
 // Function is a dl-loaded function from a dl-opened library
-type Function func(args ...interface{}) Value
-
-// func Function(args ...interface{}) retvalue
-// retvalue.Int()
-
-type Value reflect.Value
-func valueOf(o interface{}) Value {
-	return Value(reflect.ValueOf(o))
-}
+type Function func(args ...interface{}) reflect.Value
 
 type cfct struct {
 	addr unsafe.Pointer
 }
 
-var nil_fct Function = func(args ...interface{}) Value {
+var nil_fct Function = func(args ...interface{}) reflect.Value {
 	panic("ffi: nil_fct called")
 }
 
+/*
 func (lib Library) Fct(fctname string) (Function, error) {
+	println("Fct(",fctname,")...")
 	sym, err := lib.handle.Symbol(fctname)
 	if err != nil {
 		return nil_fct, err
 	}
 
-	f := cfct{unsafe.Pointer(sym)}
-	fct := func(args ...interface{}) Value {
-		if f.addr != nil {
+	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
+	cif, err := NewCif(DefaultAbi, Double, []Type{Double})
+	if err != nil {
+		return nil_fct, err
+	}
+
+	fct := func(args ...interface{}) reflect.Value {
+		println("...call.cif...")
+		out, err := cif.Call(FctPtr{addr}, args...)
+		if err != nil {
+			panic(err)
 		}
-		return valueOf(0)
+		println("...call.cif...[done]")
+		return out
+	}
+	return Function(fct), nil
+}
+*/
+
+func (lib Library) Fct(fctname string, rtype Type, argtypes []Type) (Function, error) {
+	//println("Fct(",fctname,")...")
+	sym, err := lib.handle.Symbol(fctname)
+	if err != nil {
+		return nil_fct, err
+	}
+
+	addr := (C._go_ffi_fctptr_t)(unsafe.Pointer(sym))
+	cif, err := NewCif(DefaultAbi, rtype, argtypes)
+	if err != nil {
+		return nil_fct, err
+	}
+
+	fct := func(args ...interface{}) reflect.Value {
+		//println("...call.cif...")
+		out, err := cif.Call(FctPtr{addr}, args...)
+		if err != nil {
+			panic(err)
+		}
+		//println("...call.cif...[done]")
+		return out
 	}
 	return Function(fct), nil
 }

File pkg/ffi/ffi_test.go

 
 type info struct {
 	fct string // fct name
-	arg float32
-	res float32 // expected value
+	arg float64
+	res float64 // expected value
 }
 
 func TestFFIMath(t *testing.T) {
 	if err != nil {
 		t.Errorf("%v", err)
 	}
-	
+
 	tests := []info{
-		{"cos", 0., 1.},
-		{"cos", math.Pi/2., 0.},
+		{"cos", 0., math.Cos(0.)},
+		{"cos", math.Pi / 2., math.Cos(math.Pi / 2.)},
+		{"sin", 0., math.Sin(0.)},
+		{"sin", math.Pi / 2., math.Sin(math.Pi / 2.)},
 	}
 
-	for _,info := range tests {
-		f, err := lib.Fct(info.fct)
+	for _, info := range tests {
+		f, err := lib.Fct(info.fct, ffi.Double, []ffi.Type{ffi.Double})
 		if err != nil {
 			t.Errorf("could not locate function [%s]: %v", info.fct, err)
 		}
-		f(info.arg)
-		
+		out := f(info.arg).Float()
+		if math.Abs(out-info.res) > 1e-16 {
+			t.Errorf("expected [%v], got [%v] (fct=%v(%v))", info.res, out, info.fct, info.arg)
+		}
+
 	}
 
 	err = lib.Close()
 	}
 }
 
-
 // EOF