Skip to content

Commit b237d1d

Browse files
corona10sbinet
authored andcommitted
bind/cffi: support built-in maps
* Support built-in maps. * Support maps as arguments of a function. * Support python dictionary as function parameters. * Add 'maps' test
1 parent 7138b3b commit b237d1d

File tree

7 files changed

+212
-6
lines changed

7 files changed

+212
-6
lines changed

_examples/maps/maps.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,42 @@
44

55
package maps
66

7-
func MapsFunc(t map[string]int) {
7+
import (
8+
"sort"
9+
)
810

11+
func Sum(t map[int]float64) float64 {
12+
sum := 0.0
13+
for _, v := range t {
14+
sum += v
15+
}
16+
17+
return sum
18+
}
19+
20+
func New() map[int]float64 {
21+
return map[int]float64{
22+
1: 3.0,
23+
2: 5.0,
24+
}
925
}
1026

11-
func MapsFunc2() map[int]string {
12-
return map[int]string{
13-
1: "hello",
14-
2: "world",
27+
func Keys(t map[int]float64) []int {
28+
var keys []int
29+
for k, _ := range t {
30+
keys = append(keys, k)
1531
}
32+
33+
sort.Ints(keys)
34+
return keys
35+
}
36+
37+
func Values(t map[int]float64) []float64 {
38+
var values []float64
39+
for _, v := range t {
40+
values = append(values, v)
41+
}
42+
43+
sort.Float64s(values)
44+
return values
1645
}

_examples/maps/test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2017 The go-python Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
from __future__ import print_function
6+
import maps
7+
8+
a = maps.New()
9+
b = {1: 3.0, 2: 5.0}
10+
print('maps.Sum from Go map:', maps.Sum(a))
11+
print('maps.Sum from Python dictionary:', maps.Sum(b))
12+
print('maps.Keys from Go map:', maps.Keys(a))
13+
print('maps.Values from Go map:', maps.Values(a))
14+
print('maps.Keys from Python dictionary:', maps.Keys(b))
15+
print('maps.Values from Python dictionary:', maps.Values(b))

bind/gencffi_cdef.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ func (g *cffiGen) genTypeCdefInit(sym *symbol) {
8383
g.wrapper.Printf("extern void cgo_func_%[1]s_append(void* p0, %[2]s p1);\n", sym.id, elemType.cgoname)
8484
g.wrapper.Printf("extern %[1]s cgo_func_%[2]s_item(void* p0, %[1]s p1);\n", elemType.cgoname, sym.id)
8585
case sym.isMap():
86+
ktyp := sym.GoType().Underlying().(*types.Map).Key()
87+
etyp := sym.GoType().Underlying().(*types.Map).Elem()
88+
ksym := g.pkg.syms.symtype(ktyp)
89+
esym := g.pkg.syms.symtype(etyp)
90+
g.wrapper.Printf("extern void cgo_func_%[1]s_set(void* p0, %[2]s p1, %[3]s p2);\n", sym.id, ksym.cgoname, esym.cgoname)
91+
g.wrapper.Printf("extern %[3]s cgo_func_%[1]s_get(void* p0, %[2]s p1);\n", sym.id, ksym.cgoname, esym.cgoname)
92+
g.wrapper.Printf("extern GoInt cgo_func_%[1]s_len(void* p0);\n", sym.id)
8693
case sym.isSignature():
8794
case sym.isInterface():
8895
default:

bind/gencffi_type.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ func (g *cffiGen) genTypeGetItem(sym *symbol) {
108108
esym := g.pkg.syms.symtype(typ.Elem())
109109
g.wrapper.Printf("item = _cffi_helper.lib.cgo_func_%[1]s_item(self.cgopy, idx)\n", sym.id)
110110
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(item)\n", esym.c2py)
111+
case sym.isMap():
112+
typ := sym.GoType().Underlying().(*types.Map)
113+
ksym := g.pkg.syms.symtype(typ.Key())
114+
vsym := g.pkg.syms.symtype(typ.Elem())
115+
g.wrapper.Printf("pykey = _cffi_helper.cffi_%[1]s(idx)\n", ksym.py2c)
116+
g.wrapper.Printf("item = _cffi_helper.lib.cgo_func_%[1]s_get(self.cgopy, pykey)\n", sym.id)
117+
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(item)\n", vsym.c2py)
111118
default:
112119
panic(fmt.Errorf(
113120
"gopy: __getitem__ for %s not handled",
@@ -181,6 +188,22 @@ func (g *cffiGen) genTypeInit(sym *symbol) {
181188
g.wrapper.Outdent()
182189
g.wrapper.Outdent()
183190
case sym.isMap():
191+
g.wrapper.Printf("if args:\n")
192+
g.wrapper.Indent()
193+
g.wrapper.Printf("if not isinstance(args[0], collections.Mapping):\n")
194+
g.wrapper.Indent()
195+
g.wrapper.Printf("raise TypeError('%[1]s.__init__ takes a mapping as argument')\n", sym.goname)
196+
g.wrapper.Outdent()
197+
typ := sym.GoType().Underlying().(*types.Map)
198+
esym := g.pkg.syms.symtype(typ.Elem())
199+
ksym := g.pkg.syms.symtype(typ.Key())
200+
g.wrapper.Printf("for k, v in args[0].items():\n")
201+
g.wrapper.Indent()
202+
g.wrapper.Printf("pykey = _cffi_helper.cffi_%[1]s(k)\n", ksym.py2c)
203+
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(v)\n", esym.py2c)
204+
g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s_set(self.cgopy, pykey, pyitem)\n", sym.id)
205+
g.wrapper.Outdent()
206+
g.wrapper.Outdent()
184207
case sym.isSignature():
185208
//TODO(corona10)
186209
case sym.isInterface():
@@ -205,6 +228,8 @@ func (g *cffiGen) genTypeLen(sym *symbol) {
205228
g.wrapper.Printf("return %[1]d\n", typ.Len())
206229
case sym.isSlice():
207230
g.wrapper.Printf("return ffi.cast('GoSlice*', self.cgopy).len\n")
231+
case sym.isMap():
232+
g.wrapper.Printf("return _cffi_helper.lib.cgo_func_%[1]s_len(self.cgopy)\n", sym.id)
208233
default:
209234
panic(fmt.Errorf(
210235
"gopy: len for %s not handled",
@@ -269,6 +294,22 @@ func (g *cffiGen) genTypeConverter(sym *symbol) {
269294
g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s_append(c, pyitem)\n", sym.id)
270295
g.wrapper.Outdent()
271296
g.wrapper.Printf("return c\n")
297+
case sym.isMap():
298+
g.wrapper.Printf("if not isinstance(o, collections.Mapping):\n")
299+
g.wrapper.Indent()
300+
g.wrapper.Printf("raise TypeError('%[1]s.__init__ takes a mapping as argument')\n", sym.goname)
301+
g.wrapper.Outdent()
302+
typ := sym.GoType().Underlying().(*types.Map)
303+
esym := g.pkg.syms.symtype(typ.Elem())
304+
ksym := g.pkg.syms.symtype(typ.Key())
305+
g.wrapper.Printf("c = _cffi_helper.lib.cgo_func_%[1]s_new()\n", sym.id)
306+
g.wrapper.Printf("for k, v in o.items():\n")
307+
g.wrapper.Indent()
308+
g.wrapper.Printf("pykey = _cffi_helper.cffi_%[1]s(k)\n", ksym.py2c)
309+
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(v)\n", esym.py2c)
310+
g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s_set(c, pykey, pyitem)\n", sym.id)
311+
g.wrapper.Outdent()
312+
g.wrapper.Printf("return c\n")
272313
}
273314
g.wrapper.Outdent()
274315
g.wrapper.Printf("\n")
@@ -352,6 +393,13 @@ func (g *cffiGen) genTypeSetItem(sym *symbol) {
352393
esym := g.pkg.syms.symtype(typ.Elem())
353394
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(item)\n", esym.py2c)
354395
g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s_ass_item(self.cgopy, idx, pyitem)\n", sym.id)
396+
case sym.isMap():
397+
typ := sym.GoType().Underlying().(*types.Map)
398+
ksym := g.pkg.syms.symtype(typ.Key())
399+
vsym := g.pkg.syms.symtype(typ.Elem())
400+
g.wrapper.Printf("pykey = _cffi_helper.cffi_%[1]s(idx)\n", ksym.py2c)
401+
g.wrapper.Printf("pyitem = _cffi_helper.cffi_%[1]s(item)\n", vsym.py2c)
402+
g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s_set(self.cgopy, pykey, pyitem)\n", sym.id)
355403
default:
356404
panic(fmt.Errorf(
357405
"gopy: __setitem__ for %s not handled",

bind/gengo.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ func (g *goGen) genType(sym *symbol) {
752752
g.Printf("}\n\n")
753753
}
754754

755+
g.genTypeTPMapping(sym)
755756
g.genTypeTPCall(sym)
756757

757758
g.genTypeMethods(sym)
@@ -872,6 +873,97 @@ func (g *goGen) genTypeTPCall(sym *symbol) {
872873

873874
}
874875

876+
func (g *goGen) genTypeTPMapping(sym *symbol) {
877+
if !sym.isMap() {
878+
return
879+
}
880+
881+
ktyp := sym.GoType().Underlying().(*types.Map).Key()
882+
ksym := g.pkg.syms.symtype(ktyp)
883+
etyp := sym.GoType().Underlying().(*types.Map).Elem()
884+
esym := g.pkg.syms.symtype(etyp)
885+
886+
if ksym == nil {
887+
panic(fmt.Errorf("gopy: could not retrieve key type of %v", sym))
888+
}
889+
890+
if esym == nil {
891+
panic(fmt.Errorf("gopy: could not retrieve element type of %v", sym))
892+
}
893+
894+
g.Printf("//export cgo_func_%[1]s_set\n", sym.id)
895+
g.Printf("func cgo_func_%[1]s_set(self %[2]s, key %[3]s, value %[4]s) {\n",
896+
sym.id,
897+
sym.cgoname,
898+
ksym.cgotypename(),
899+
esym.cgotypename(),
900+
)
901+
g.Indent()
902+
g.Printf("m := (*%[1]s)(unsafe.Pointer(self))\n", sym.gofmt())
903+
g.Printf("if *m == nil {\n")
904+
g.Indent()
905+
g.Printf("*m = make(map[%[1]s]%[2]s)\n", ksym.cgotypename(), esym.cgotypename())
906+
g.Outdent()
907+
g.Printf("}\n")
908+
if !ksym.isBasic() {
909+
g.Printf("k := *(*%[1]s)(unsafe.Pointer(key))", ksym.gofmt())
910+
} else {
911+
if ksym.isNamed() {
912+
g.Printf("k := %[1]s(key)\n", esym.gofmt())
913+
} else {
914+
g.Printf("k := key\n")
915+
}
916+
}
917+
918+
if !esym.isBasic() {
919+
g.Printf("v := *(*%[1]s)(unsafe.Pointer(value))", esym.gofmt())
920+
} else {
921+
if esym.isNamed() {
922+
g.Printf("v := %[1]s(value)", esym.gofmt())
923+
} else {
924+
g.Printf("v := value\n")
925+
}
926+
}
927+
928+
g.Printf("(*m)[k] = v\n")
929+
g.Outdent()
930+
g.Printf("}\n\n")
931+
932+
g.Printf("//export cgo_func_%[1]s_get\n", sym.id)
933+
g.Printf("func cgo_func_%[1]s_get(self %[2]s, key %[3]s) %[4]s {\n",
934+
sym.id,
935+
sym.cgoname,
936+
ksym.cgotypename(),
937+
esym.cgotypename(),
938+
)
939+
g.Indent()
940+
g.Printf("m := (*%[1]s)(unsafe.Pointer(self))\n", sym.gofmt())
941+
942+
if !ksym.isBasic() {
943+
g.Printf("k := *(*%[1]s)(unsafe.Pointer(key))\n", ksym.gofmt())
944+
} else {
945+
if ksym.isNamed() {
946+
g.Printf("k := %[1]s(key)\n", esym.gofmt())
947+
} else {
948+
g.Printf("k := key\n")
949+
}
950+
}
951+
g.Printf("return (*m)[k]\n")
952+
g.Outdent()
953+
g.Printf("}\n\n")
954+
955+
g.Printf("//export cgo_func_%[1]s_len\n", sym.id)
956+
g.Printf("func cgo_func_%[1]s_len(self %[2]s) int {\n",
957+
sym.id,
958+
sym.cgoname,
959+
)
960+
g.Indent()
961+
g.Printf("m := (*%[1]s)(unsafe.Pointer(self))\n", sym.gofmt())
962+
g.Printf("return len(*m)\n")
963+
g.Outdent()
964+
g.Printf("}\n\n")
965+
}
966+
875967
func (g *goGen) genTypeMethods(sym *symbol) {
876968
if !sym.isNamed() {
877969
return

bind/symtab.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func (s symbol) isMap() bool {
131131
}
132132

133133
func (s symbol) isPySequence() bool {
134-
return s.isArray() || s.isSlice()
134+
return s.isArray() || s.isSlice() || s.isMap()
135135
}
136136

137137
func (s symbol) isSlice() bool {

main_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,21 @@ slices.IntSum from Go slice: 10
753753
})
754754
}
755755

756+
func TestBuiltinMaps(t *testing.T) {
757+
t.Parallel()
758+
testPkg(t, pkg{
759+
path: "_examples/maps",
760+
lang: []string{"py2-cffi", "py3-cffi", "pypy2-cffi", "pypy3-cffi"},
761+
want: []byte(`maps.Sum from Go map: 8.0
762+
maps.Sum from Python dictionary: 8.0
763+
maps.Keys from Go map: []int{1, 2}
764+
maps.Values from Go map: []float64{3, 5}
765+
maps.Keys from Python dictionary: []int{1, 2}
766+
maps.Values from Python dictionary: []float64{3, 5}
767+
`),
768+
})
769+
}
770+
756771
func TestBindStrings(t *testing.T) {
757772
t.Parallel()
758773
testPkg(t, pkg{

0 commit comments

Comments
 (0)