Skip to content

Adding support for variadic functions. #279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions _examples/variadic/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2018 The go-python Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
import variadic, go

############### Non Variadic ##############
nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5)
print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult)

############### Variadic Over Int ##############
varResult = variadic.VariFunc(1,2,3,4,5)
print("Variadic 1+2+3+4+5 = %d" % varResult)

############### Variadic Over Struct ##############
varStructResult = variadic.VariStructFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3))
print("Variadic Struct s(1)+s(2)+s(3) = %d" % varStructResult)

############### Variadic Over InterFace ##############
varInterFaceResult = variadic.VariInterFaceFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3))
print("Variadic InterFace i(1)+i(2)+i(3) = %d" % varInterFaceResult)

############### Final ##############
if isinstance(varResult, int):
print("Type OK")
else:
print("Type Not OK")
57 changes: 57 additions & 0 deletions _examples/variadic/variadic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package variadic

/////////////// Non Variadic //////////////
func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{
total := arg1
for _, num := range arg2 {
total += num
}
total += arg3

return total
}

/////////////// Variadic Over Int //////////////
func VariFunc(vargs ...int) int{
total := 0
for _, num := range vargs {
total += num
}
return total
}

/////////////// Variadic Over Struct //////////////
type IntStrUct struct {
p int
}

func NewIntStrUct(n int) IntStrUct {
return IntStrUct {
p:n,
}
}

func VariStructFunc(vargs ...IntStrUct) int{
total := 0
for _, inst := range vargs {
total += inst.p
}
return total
}

/////////////// Variadic Over Interface //////////////
type IntInterFace interface {
Number() int
}

func (is *IntStrUct) Number() int {
return is.p
}

func VariInterFaceFunc(vargs ...IntInterFace) int{
total := 0
for _, inst := range vargs {
total += inst.Number()
}
return total
}
61 changes: 41 additions & 20 deletions bind/gen_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
return false
}
anm := pySafeArg(arg.Name(), i)

if ifchandle && arg.sym.goname == "interface{}" {
goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, CGoHandle))
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", PyHandle, anm))
Expand All @@ -98,7 +99,10 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, anm))
}
}
wpArgs = append(wpArgs, anm)

if i!=nargs-1 || !fsym.isVariadic {
wpArgs = append(wpArgs, anm)
}
}

// support for optional arg to run in a separate go routine -- only if no return val
Expand All @@ -108,6 +112,11 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
wpArgs = append(wpArgs, "goRun=False")
}

// To support variadic args, we add *args at the end.
if fsym.isVariadic {
wpArgs = append(wpArgs, "*args")
}

// When building the pybindgen builder code, we start with
// a function that adds function calls with exception checking.
// But given specific return types, we may want to add more
Expand Down Expand Up @@ -276,25 +285,6 @@ if __err != nil {
g.gofile.Printf("var __err error\n")
}

// pywrap output
mnm := fsym.ID()
if isMethod {
mnm = sym.id + "_" + fsym.GoName()
}
rvHasHandle := false
if nres > 0 {
ret := res[0]
if !rvIsErr && ret.sym.hasHandle() {
rvHasHandle = true
cvnm := ret.sym.pyPkgId(g.pkg.pkg)
g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm)
} else {
g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
}
} else {
g.pywrap.Printf("_%s.%s(", pkgname, mnm)
}

callArgs := []string{}
wrapArgs := []string{}
if isMethod {
Expand All @@ -313,6 +303,9 @@ if __err != nil {
default:
na = anm
}
if i == len(args) - 1 && fsym.isVariadic {
na = na + "..."
}
callArgs = append(callArgs, na)
switch {
case arg.sym.goname == "interface{}":
Expand All @@ -326,6 +319,34 @@ if __err != nil {
default:
wrapArgs = append(wrapArgs, anm)
}

// To support variadic args, we add *args at the end.
if fsym.isVariadic && i == len(args)-1 {
packagePrefix := ""
if arg.sym.gopkg.Name() != fsym.pkg.Name() {
packagePrefix = arg.sym.gopkg.Name() + "."
}
g.pywrap.Printf("%s = %s%s(args)\n", anm, packagePrefix, arg.sym.id)
}
}

// pywrap output
mnm := fsym.ID()
if isMethod {
mnm = sym.id + "_" + fsym.GoName()
}
rvHasHandle := false
if nres > 0 {
ret := res[0]
if !rvIsErr && ret.sym.hasHandle() {
rvHasHandle = true
cvnm := ret.sym.pyPkgId(g.pkg.pkg)
g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm)
} else {
g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
}
} else {
g.pywrap.Printf("_%s.%s(", pkgname, mnm)
}

hasRetCvt := false
Expand Down
5 changes: 0 additions & 5 deletions bind/symbols.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,6 @@ func isPyCompatField(f *types.Var) (*symbol, error) {
func isPyCompatFunc(sig *types.Signature) (ret types.Type, haserr, hasfun bool, err error) {
res := sig.Results()

if sig.Variadic() {
err = fmt.Errorf("gopy: not yet supporting variadic functions: %s", sig.String())
return
}

switch res.Len() {
case 2:
if !isErrorType(res.At(1).Type()) {
Expand Down
34 changes: 18 additions & 16 deletions bind/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,13 @@ type Func struct {
obj types.Object
name string

id string
doc string
ret types.Type // return type, if any
err bool // true if original go func has comma-error
ctor bool // true if this is a newXXX function
hasfun bool // true if this function has a function argument
id string
doc string
ret types.Type // return type, if any
err bool // true if original go func has comma-error
ctor bool // true if this is a newXXX function
hasfun bool // true if this function has a function argument
isVariadic bool // True, if this is a variadic function.
}

func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signature) (*Func, error) {
Expand All @@ -392,16 +393,17 @@ func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signatu
}

return &Func{
obj: obj,
pkg: p,
sig: sv,
typ: obj.Type(),
name: obj.Name(),
id: id,
doc: p.getDoc(parent, obj),
ret: ret,
err: haserr,
hasfun: hasfun,
obj: obj,
pkg: p,
sig: sv,
typ: obj.Type(),
name: obj.Name(),
id: id,
doc: p.getDoc(parent, obj),
ret: ret,
err: haserr,
hasfun: hasfun,
isVariadic: sig.Variadic(),
}, nil

// TODO: could optimize by generating code once for each type of callback
Expand Down
18 changes: 18 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var (
"_examples/gopygc": []string{"py2", "py3"},
"_examples/cstrings": []string{"py2", "py3"},
"_examples/pkgconflict": []string{"py2", "py3"},
"_examples/variadic": []string{"py3"},
}

testEnvironment = os.Environ()
Expand Down Expand Up @@ -811,6 +812,23 @@ func TestPkgConflict(t *testing.T) {
// })
// }

func TestBindVariadic(t *testing.T) {
// t.Parallel()
path := "_examples/variadic"
testPkg(t, pkg{
path: path,
lang: features[path],
cmd: "build",
extras: nil,
want: []byte(`NonVariadic 1+[2+3+4]+5 = 15
Variadic 1+2+3+4+5 = 15
Variadic Struct s(1)+s(2)+s(3) = 6
Variadic InterFace i(1)+i(2)+i(3) = 6
Type OK
`),
})
}

// Generate / verify SUPPORT_MATRIX.md from features map.
func TestCheckSupportMatrix(t *testing.T) {
var buf bytes.Buffer
Expand Down