python callback fully implemented -- one last thing is dealing with return values properly..

This commit is contained in:
Randall C. O'Reilly 2019-02-28 12:19:14 -07:00 committed by Sebastien Binet
parent 179e37fa2d
commit 697577b7f4
6 changed files with 128 additions and 49 deletions

View File

@ -8,13 +8,20 @@ import "fmt"
type FunStruct struct {
FieldI int
FieldS string
}
func (fs *FunStruct) CallBack(arg1 int, fun func(a1 int)) {
fun(arg1)
func (fs *FunStruct) CallBack(arg1 int, fun func(afs *FunStruct, a1 int, s1 string)) {
fun(fs, arg1, fs.FieldS)
}
func (fs *FunStruct) CallBackIf(arg1 int, fun func(afs *FunStruct, a1 int, if1 interface{})) {
fun(fs, arg1, fs.FieldS)
}
func (fs *FunStruct) OtherMeth(arg1 int, args string) {
fs.FieldI = arg1
fs.FieldS = args
fmt.Printf("arg1: %d args: %s\n", arg1, args)
}

View File

@ -7,23 +7,33 @@ from __future__ import print_function
from funcs import go, funcs
def cbfun(val):
print("in python cbfun: val", val)
def cbfun(afs, ival, sval):
tfs = funcs.FunStruct(handle=afs)
print("in python cbfun: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " sval: ", sval)
def cbfunif(afs, ival, ifval):
tfs = funcs.FunStruct(handle=afs)
print("in python cbfunif: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " ifval: ", ifval)
class MyClass(object):
def __init__(self, *args, **kwargs):
self.misc = 2
def ClassFun(self, val):
print("in python class fun: val", val)
def ClassFun(self, afs, ival, sval):
tfs = funcs.FunStruct(handle=afs)
print("in python class fun: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " sval: ", sval)
fs = funcs.FunStruct()
fs.FieldS = "str field"
fs.FieldI = 42
fs.CallBack(22, cbfun)
fs.CallBackIf(22, cbfunif)
cls = MyClass()
fs.CallBack(22, cls.ClassFun)
fs.CallBack(32, cls.ClassFun)
# print("funcs.GetF1()...")
# f1 = funcs.GetF1()

View File

@ -44,14 +44,20 @@ package main
// btw, static inline is trick for avoiding need for extra .c file
// the following are used for build value -- switch on reflect.Kind
// or the types equivalent
static inline PyObject* py_build_int64(int64_t val) {
static inline PyObject* gopy_build_bool(uint8_t val) {
return Py_BuildValue("b", val);
}
static inline PyObject* gopy_build_int64(int64_t val) {
return Py_BuildValue("k", val);
}
static inline PyObject* py_build_float64(double val) {
static inline PyObject* gopy_build_uint64(uint64_t val) {
return Py_BuildValue("K", val);
}
static inline PyObject* gopy_build_float64(double val) {
return Py_BuildValue("d", val);
}
static inline PyObject* py_build_string(const char* val) {
return Py_BuildValue("d", val);
static inline PyObject* gopy_build_string(const char* val) {
return Py_BuildValue("s", val);
}
*/
import "C"

View File

@ -12,6 +12,9 @@ func (g *pyGen) genConst(c *Const) {
if isPyCompatVar(c.sym) != nil {
return
}
if c.sym.isSignature() {
return
}
g.genConstValue(c)
}
@ -19,6 +22,9 @@ func (g *pyGen) genVar(v *Var) {
if isPyCompatVar(v.sym) != nil {
return
}
if v.sym.isSignature() {
return
}
g.genVarGetter(v)
if !v.sym.isArray() {
g.genVarSetter(v)

View File

@ -320,7 +320,7 @@ func stdBasicTypes() map[string]*symbol {
go2py: "C.CString",
py2go: "C.GoString",
zval: `""`,
pyfmt: "O&",
pyfmt: "s",
},
"rune": { // FIXME(sbinet) py2/py3

View File

@ -100,9 +100,6 @@ func isPyCompatVar(v *symbol) error {
if v == nil {
return fmt.Errorf("gopy: var symbol not found")
}
// if v.isSignature() {
// return fmt.Errorf("gopy: var is function signature")
// }
if v.isPointer() && v.isBasic() {
return fmt.Errorf("gopy: var is pointer to basic type")
}
@ -120,9 +117,7 @@ func isPyCompatVar(v *symbol) error {
// isPyCompatType checks if type is compatible with python
func isPyCompatType(typ types.Type) error {
// if _, isSig := typ.(*types.Signature); isSig {
// return fmt.Errorf("gopy: type is function signature")
// }
typ = typ.Underlying()
if ptyp, isPtr := typ.(*types.Pointer); isPtr {
if _, isBasic := ptyp.Elem().(*types.Basic); isBasic {
return fmt.Errorf("gopy: type is pointer to basic type")
@ -146,6 +141,9 @@ func isPyCompatField(f *types.Var) (error, *symbol) {
return fmt.Errorf("gopy: field not exported or is embedded"), nil
}
ftyp := current.symtype(f.Type())
if _, isSig := f.Type().Underlying().(*types.Signature); isSig {
return fmt.Errorf("gopy: type is function signature"), nil
}
return isPyCompatVar(ftyp), ftyp
}
@ -189,6 +187,9 @@ func isPyCompatFunc(sig *types.Signature) (err error, ret types.Type, haserr, ha
if err = isPyCompatType(ret); err != nil {
return
}
if _, isSig := ret.Underlying().(*types.Signature); isSig {
return
}
}
args := sig.Params()
@ -199,7 +200,7 @@ func isPyCompatFunc(sig *types.Signature) (err error, ret types.Type, haserr, ha
if err = isPyCompatType(argt); err != nil {
return
}
if _, isSig := argt.(*types.Signature); isSig {
if _, isSig := argt.Underlying().(*types.Signature); isSig {
if !hasfun {
hasfun = true
} else {
@ -554,6 +555,7 @@ func (sym *symtab) addSymbol(obj types.Object) error {
return nil
}
// processTuple ensures that all types in a tuple are in sym table
func (sym *symtab) processTuple(tuple *types.Tuple) error {
if tuple == nil {
return nil
@ -572,6 +574,55 @@ func (sym *symtab) processTuple(tuple *types.Tuple) error {
return nil
}
// buildTuple returns a string of Go code that builds a PyTuple
// for the given tuple (e.g., function args).
// varnm is the name of the local variable for the built tuple
func (sym *symtab) buildTuple(tuple *types.Tuple, varnm string) (string, error) {
sz := tuple.Len()
if sz == 0 {
return "", fmt.Errorf("buildTuple: no elements")
}
bstr := fmt.Sprintf("%s := C.PyTuple_New(%d); ", varnm, sz)
for i := 0; i < sz; i++ {
v := tuple.At(i)
typ := v.Type()
anm := pySafeArg(v.Name(), i)
vsym := sym.symtype(typ)
if vsym == nil {
err := sym.addType(v, typ)
if err != nil {
return "", err
}
vsym = sym.symtype(typ)
if vsym == nil {
return "", fmt.Errorf("buildTuple: type still not found: %s", typ.String())
}
}
bt, isb := typ.Underlying().(*types.Basic)
switch {
case vsym.hasHandle(): // note: assuming int64 handles
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_int64(C.int64_t(%s(%s)%s))); ", varnm, i, vsym.go2py, anm, vsym.go2pyParenEx)
case isb:
bk := bt.Kind()
switch {
case bk >= types.Int && bk <= types.Int64:
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_int64(C.int64_t(%s))); ", varnm, i, anm)
case bk >= types.Uint && bk <= types.Uintptr:
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_uint64(C.uint64_t(%s))); ", varnm, i, anm)
case bk >= types.Float32 && bk <= types.Float64:
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_float64(C.double(%s))); ", varnm, i, anm)
case bk == types.String:
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_string(C.CString(%s))); ", varnm, i, anm)
case bk == types.Bool:
bstr += fmt.Sprintf("C.PyTuple_SetItem(%s, %d, C.gopy_build_bool(C.uint8_t(boolGoToPy(%s)))); ", varnm, i, anm)
}
default:
return "", fmt.Errorf("buildTuple: type not handled: %s", typ.String())
}
}
return bstr, nil
}
// typeNamePkg gets the goname and package for a given types.Type -- deals with
// naming for types in other packages, and adds those packages to imports paths.
// Falls back on sym.pkg if no other package info avail.
@ -879,44 +930,43 @@ func (sym *symtab) addStructType(pkg *types.Package, obj types.Object, t types.T
}
func (sym *symtab) addSignatureType(pkg *types.Package, obj types.Object, t types.Type, kind symkind, id, n string) error {
fn := sym.fullTypeString(t)
fn := sym.fullTypeString(t.Underlying())
kind |= skSignature
// if (kind & skNamed) == 0 {
// id = hash(id)
// }
py2g := fmt.Sprintf("%s { ", fn)
py2g := fmt.Sprintf("%s { ", n)
sig := t.(*types.Signature)
sig := t.Underlying().(*types.Signature)
args := sig.Params()
rets := sig.Results()
if rets.Len() > 0 { // todo 1
return fmt.Errorf("multiple return values not supported")
}
retstr := ""
if rets.Len() == 1 {
retstr = "_fcret := "
}
nargs := args.Len()
// todo: this va_list thing is when you're in a var arg function
// inside of c -- not for building dynamically
// need to use the ... var args version in C -- need to put
// all this arg building code into c.. :(
if nargs > 0 {
py2g += fmt.Sprintf("_fcargs := C.PyTuple_New(%d); ", nargs)
for i := 0; i < nargs; i++ {
arg := args.At(i)
argt := arg.Type()
anm := pySafeArg(arg.Name(), i)
asym := sym.symtype(argt)
if asym == nil {
err := sym.addType(arg, argt)
if err != nil {
return err
}
asym = sym.symtype(argt)
if asym == nil {
return fmt.Errorf("type still not found: %s", argt.String())
}
}
// todo: switch on type
py2g += fmt.Sprintf("C.PyTuple_SetItem(_fcargs, %d, C.py_build_int64(C.int64_t(%s))); ", i, anm)
bstr, err := sym.buildTuple(args, "_fcargs")
if err != nil {
return err
}
py2g += fmt.Sprintf("C.PyObject_CallObject(_fun_arg, _fcargs) ")
py2g += bstr + retstr
py2g += fmt.Sprintf("C.PyObject_CallObject(_fun_arg, _fcargs); ")
} else {
py2g += "C.PyObject_CallObject(_fun_arg, nil) "
py2g += retstr + "C.PyObject_CallObject(_fun_arg, nil); "
}
if rets.Len() == 1 {
ret := rets.At(0)
rsym := sym.symtype(ret.Type())
if rsym == nil {
return fmt.Errorf("return type not supported: %s", n)
}
if rsym.py2go != "" {
py2g += fmt.Sprintf("return %s(_fcret)%s", rsym.py2go, rsym.py2goParenEx)
} else {
py2g += "return _fcret"
}
}
py2g += "}"