func Test5603(t *testing.T) { test5603(t) }
func Test3250(t *testing.T) { test3250(t) }
func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
+func TestFpVar(t *testing.T) { testFpVar(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains test cases for cgo with function pointer variables.
+
+package cgotest
+
+/*
+typedef int (*intFunc) ();
+
+int
+bridge_int_func(intFunc f)
+{
+ return f();
+}
+
+int fortytwo()
+{
+ return 42;
+}
+
+*/
+import "C"
+import "testing"
+
+func callBridge(f C.intFunc) int {
+ return int(C.bridge_int_func(f))
+}
+
+func callCBridge(f C.intFunc) C.int {
+ return C.bridge_int_func(f)
+}
+
+func testFpVar(t *testing.T) {
+ const expected = 42
+ f := C.intFunc(C.fortytwo)
+ res1 := C.bridge_int_func(f)
+ if r1 := int(res1); r1 != expected {
+ t.Errorf("got %d, want %d", r1, expected)
+ }
+ res2 := callCBridge(f)
+ if r2 := int(res2); r2 != expected {
+ t.Errorf("got %d, want %d", r2, expected)
+ }
+ r3 := callBridge(f)
+ if r3 != expected {
+ t.Errorf("got %d, want %d", r3, expected)
+ }
+}
n, err := C.sqrt(-1)
_, err := C.voidFunc()
+Calling C function pointers is currently not supported, however you can
+declare Go variables which hold C function pointers and pass them
+back and forth between Go and C. C code may call function pointers
+received from Go. For example:
+
+ package main
+ // typedef int (*intFunc) ();
+ //
+ // int
+ // bridge_int_func(intFunc f)
+ // {
+ // return f();
+ // }
+ //
+ // int fortytwo()
+ // {
+ // return 42;
+ // }
+ import "C"
+ import "fmt"
+
+ func main() {
+ f := C.intFunc(C.fortytwo)
+ fmt.Println(int(C.bridge_int_func(f)))
+ // Output: 42
+ }
+
In C, a function argument written as a fixed size array
actually requires a pointer to the first element of the array.
C compilers are aware of this calling convention and adjust
}
+// mangleName does name mangling to translate names
+// from the original Go source files to the names
+// used in the final Go files generated by cgo.
+func (p *Package) mangleName(n *Name) {
+ // When using gccgo variables have to be
+ // exported so that they become global symbols
+ // that the C code can refer to.
+ prefix := "_C"
+ if *gccgo && n.IsVar() {
+ prefix = "C"
+ }
+ n.Mangle = prefix + n.Kind + "_" + n.Go
+}
+
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
// Go equivalents, now that we have figured out the meaning of all
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
// with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) {
+ // Keep a list of all the functions, to remove the ones
+ // only used as expressions and avoid generating bridge
+ // code for them.
+ functions := make(map[string]bool)
+
// Assign mangled names.
for _, n := range f.Name {
if n.Kind == "not-type" {
n.Kind = "var"
}
if n.Mangle == "" {
- // When using gccgo variables have to be
- // exported so that they become global symbols
- // that the C code can refer to.
- prefix := "_C"
- if *gccgo && n.Kind == "var" {
- prefix = "C"
- }
- n.Mangle = prefix + n.Kind + "_" + n.Go
+ p.mangleName(n)
+ }
+ if n.Kind == "func" {
+ functions[n.Go] = false
}
}
error_(r.Pos(), "call of non-function C.%s", r.Name.Go)
break
}
+ functions[r.Name.Go] = true
if r.Context == "call2" {
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
}
case "expr":
if r.Name.Kind == "func" {
- error_(r.Pos(), "must call C.%s", r.Name.Go)
- }
- if r.Name.Kind == "type" {
+ // Function is being used in an expression, to e.g. pass around a C function pointer.
+ // Create a new Name for this Ref which causes the variable to be declared in Go land.
+ fpName := "fp_" + r.Name.Go
+ name := f.Name[fpName]
+ if name == nil {
+ name = &Name{
+ Go: fpName,
+ C: r.Name.C,
+ Kind: "fpvar",
+ Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
+ }
+ p.mangleName(name)
+ f.Name[fpName] = name
+ }
+ r.Name = name
+ expr = ast.NewIdent(name.Mangle)
+ } else if r.Name.Kind == "type" {
// Okay - might be new(T)
expr = r.Name.Type.Go
- }
- if r.Name.Kind == "var" {
+ } else if r.Name.Kind == "var" {
expr = &ast.StarExpr{X: expr}
}
}
*r.Expr = expr
}
+
+ // Remove functions only used as expressions, so their respective
+ // bridge functions are not generated.
+ for name, used := range functions {
+ if !used {
+ delete(f.Name, name)
+ }
+ }
}
// gccBaseCmd returns the start of the compiler command line.
Mangle string // name used in generated Go
C string // name used in C
Define string // #define expansion
- Kind string // "const", "type", "var", "func", "not-type"
+ Kind string // "const", "type", "var", "fpvar", "func", "not-type"
Type *Type // the type of xxx
FuncType *FuncType
AddError bool
Const string // constant definition
}
+// IsVar returns true if Kind is either "var" or "fpvar"
+func (n *Name) IsVar() bool {
+ return n.Kind == "var" || n.Kind == "fpvar"
+}
+
// A ExpFunc is an exported function, callable from C.
// Such functions are identified in the Go input file
// by doc comments containing the line //export ExpName
cVars := make(map[string]bool)
for _, key := range nameKeys(p.Name) {
n := p.Name[key]
- if n.Kind != "var" {
+ if !n.IsVar() {
continue
}
cVars[n.C] = true
}
-
+ var amp string
+ var node ast.Node
+ if n.Kind == "var" {
+ amp = "&"
+ node = &ast.StarExpr{X: n.Type.Go}
+ } else if n.Kind == "fpvar" {
+ node = n.Type.Go
+ } else {
+ panic(fmt.Errorf("invalid var kind %q", n.Kind))
+ }
if *gccgo {
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
- fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
+ fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C)
} else {
- fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
+ fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C)
}
fmt.Fprintf(fc, "\n")
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
- conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
+ conf.Fprint(fgo2, fset, node)
fmt.Fprintf(fgo2, "\n")
}
fmt.Fprintf(fc, "\n")