genbindings/go: refactor to reuse template emitters

This commit is contained in:
mappu 2024-09-17 19:30:06 +12:00
parent a1d6b6a543
commit 15f4c367d0
1 changed files with 83 additions and 107 deletions

View File

@ -178,6 +178,8 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
// QApplication constructor. Convert 'args' into Qt's wanted types
// Qt has a warning in the docs saying these pointers must be valid
// for the entire lifetype of QApplication, so, malloc + never free
// This transformation only affects the Go side. The CABI side is
// projected naturally
preamble += "// Convert []string to long-lived int& argc, char** argv, never call free()\n"
preamble += "argc := (*C.int)(C.malloc(8))\n"
@ -193,89 +195,84 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
// Skip this parameter, already handled
skipNext = false
} else if p.ParameterType == "QString" {
// Go: convert string -> miqt_string*
// CABI: convert miqt_string* -> real QString
preamble += p.ParameterName + "_ms := miqt_strdupg(" + p.ParameterName + ")\n"
preamble += "defer C.free(" + p.ParameterName + "_ms)\n"
tmp = append(tmp, "(*C.struct_miqt_string)("+p.ParameterName+"_ms)")
} else if listType, ok := p.QListOf(); ok {
// QList<T>
// Go: convert T[] -> t* and len
// CABI: create a real QList<>
gfs.imports["runtime"] = struct{}{}
gfs.imports["unsafe"] = struct{}{}
if listType.ParameterType == "QString" {
// Combo
preamble += "// For the C ABI, malloc two C arrays; raw char* pointers and their lengths\n"
preamble += p.ParameterName + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_CArray))\n"
preamble += "for i := range " + p.ParameterName + "{\n"
preamble += "single_ms := miqt_strdupg(" + p.ParameterName + "[i])\n"
preamble += "defer C.free(single_ms)\n"
preamble += p.ParameterName + "_CArray[i] = (*C.struct_miqt_string)(single_ms)\n"
preamble += "}\n"
preamble += p.ParameterName + "_ma := &C.struct_miqt_array{len: C.size_t(len(" + p.ParameterName + ")), data: unsafe.Pointer(" + p.ParameterName + "_CArray)}\n"
preamble += "defer runtime.KeepAlive(unsafe.Pointer(" + p.ParameterName + "_ma))\n"
tmp = append(tmp, p.ParameterName+"_ma")
} else {
preamble += "// For the C ABI, malloc a C array of raw pointers\n"
preamble += p.ParameterName + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_CArray))\n"
preamble += "for i := range " + p.ParameterName + "{\n"
if listType.QtClassType() {
preamble += p.ParameterName + "_CArray[i] = " + p.ParameterName + "[i].cPointer()\n"
} else {
preamble += p.ParameterName + "_CArray[i] = (" + listType.parameterTypeCgo() + ")(" + p.ParameterName + "[i])\n"
}
preamble += "}\n"
preamble += p.ParameterName + "_ma := &C.struct_miqt_array{len: C.size_t(len(" + p.ParameterName + ")), data: unsafe.Pointer(" + p.ParameterName + "_CArray)}\n"
preamble += "defer runtime.KeepAlive(unsafe.Pointer(" + p.ParameterName + "_ma))\n"
tmp = append(tmp, p.ParameterName+"_ma")
}
} else if p.Pointer && p.ParameterType == "char" {
// Single char* argument
gfs.imports["unsafe"] = struct{}{}
preamble += p.ParameterName + "_Cstring := C.CString(" + p.ParameterName + ")\n"
preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_Cstring))\n"
tmp = append(tmp, p.ParameterName+"_Cstring")
} else if /*(p.Pointer || p.ByRef) &&*/ p.QtClassType() {
// The C++ type is a pointer to Qt class
// We want our functions to accept the Go wrapper type, and forward as cPointer()
tmp = append(tmp, p.ParameterName+".cPointer()")
} else if p.IntType() || p.ParameterType == "bool" {
if p.Pointer || p.ByRef {
gfs.imports["unsafe"] = struct{}{}
tmp = append(tmp, "("+p.parameterTypeCgo()+")(unsafe.Pointer("+p.ParameterName+"))") // n.b. This may not work if the integer type conversion was wrong
} else {
tmp = append(tmp, "("+p.parameterTypeCgo()+")("+p.ParameterName+")")
}
} else {
// Default
tmp = append(tmp, p.ParameterName)
addPreamble, rvalue := gfs.emitParameterGo2CABIForwarding(p)
preamble += addPreamble
tmp = append(tmp, rvalue)
}
}
return preamble, strings.Join(tmp, ", ")
}
func (gfs *goFileState) emitParameterGo2CABIForwarding(p CppParameter) (preamble string, rvalue string) {
nameprefix := strings.Replace(strings.Replace(p.ParameterName, `[`, `_`, -1), `]`, "", -1)
if p.ParameterType == "QString" {
// Go: convert string -> miqt_string*
// CABI: convert miqt_string* -> real QString
preamble += nameprefix + "_ms := miqt_strdupg(" + p.ParameterName + ")\n"
preamble += "defer C.free(" + nameprefix + "_ms)\n"
rvalue = "(*C.struct_miqt_string)(" + nameprefix + "_ms)"
} else if listType, ok := p.QListOf(); ok {
// QList<T>
// Go: convert T[] -> t* and len
// CABI: create a real QList<>
gfs.imports["runtime"] = struct{}{}
gfs.imports["unsafe"] = struct{}{}
preamble += "// For the C ABI, malloc a C array of raw pointers\n"
preamble += nameprefix + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
preamble += "defer C.free(unsafe.Pointer(" + nameprefix + "_CArray))\n"
preamble += "for i := range " + p.ParameterName + "{\n"
listType.ParameterName = p.ParameterName + "[i]"
addPreamble, innerRvalue := gfs.emitParameterGo2CABIForwarding(listType)
preamble += addPreamble
preamble += nameprefix + "_CArray[i] = " + innerRvalue + "\n"
preamble += "}\n"
preamble += p.ParameterName + "_ma := &C.struct_miqt_array{len: C.size_t(len(" + p.ParameterName + ")), data: unsafe.Pointer(" + nameprefix + "_CArray)}\n"
preamble += "defer runtime.KeepAlive(unsafe.Pointer(" + nameprefix + "_ma))\n"
rvalue = p.ParameterName + "_ma"
} else if p.Pointer && p.ParameterType == "char" {
// Single char* argument
gfs.imports["unsafe"] = struct{}{}
preamble += nameprefix + "_Cstring := C.CString(" + p.ParameterName + ")\n"
preamble += "defer C.free(unsafe.Pointer(" + nameprefix + "_Cstring))\n"
rvalue = nameprefix + "_Cstring"
} else if /*(p.Pointer || p.ByRef) &&*/ p.QtClassType() {
// The C++ type is a pointer to Qt class
// We want our functions to accept the Go wrapper type, and forward as cPointer()
rvalue = p.ParameterName + ".cPointer()"
} else if p.IntType() || p.ParameterType == "bool" {
if p.Pointer || p.ByRef {
gfs.imports["unsafe"] = struct{}{}
rvalue = "(" + p.parameterTypeCgo() + ")(unsafe.Pointer(" + p.ParameterName + "))" // n.b. This may not work if the integer type conversion was wrong
} else {
rvalue = "(" + p.parameterTypeCgo() + ")(" + p.ParameterName + ")"
}
} else {
// Default
rvalue = p.ParameterName
}
return preamble, rvalue
}
func (gfs *goFileState) emitCabiToGo(assignExpr string, rt CppParameter, rvalue string) string {
shouldReturn := "return "
@ -309,37 +306,16 @@ func (gfs *goFileState) emitCabiToGo(assignExpr string, rt CppParameter, rvalue
gfs.imports["unsafe"] = struct{}{}
shouldReturn = "var " + namePrefix + "_ma *C.struct_miqt_array = "
if t.ParameterType == "QString" {
// Combo
afterword += namePrefix + "_ret := make([]string, int(" + namePrefix + "_ma.len))\n"
afterword += "_outCast := (*[0xffff]*C.struct_miqt_string)(unsafe.Pointer(" + namePrefix + "_ma.data)) // hey ya\n"
afterword += "for i := 0; i < int(" + namePrefix + "_ma.len); i++ {\n"
afterword += namePrefix + "_ret[i] = C.GoStringN(&_outCast[i].data, C.int(int64(_outCast[i].len)))\n"
afterword += "C.free(unsafe.Pointer(_outCast[i])) // free the inner miqt_string*\n"
afterword += "}\n"
afterword += "C.free(unsafe.Pointer(" + namePrefix + "_ma))\n"
afterword += assignExpr + " " + namePrefix + "_ret\n"
afterword += namePrefix + "_ret := make([]" + t.RenderTypeGo() + ", int(" + namePrefix + "_ma.len))\n"
afterword += namePrefix + "_outCast := (*[0xffff]" + t.parameterTypeCgo() + ")(unsafe.Pointer(" + namePrefix + "_ma.data)) // hey ya\n"
afterword += "for i := 0; i < int(" + namePrefix + "_ma.len); i++ {\n"
} else {
afterword += gfs.emitCabiToGo(namePrefix+"_ret[i] = ", t, namePrefix+"_outCast[i]")
afterword += "" + namePrefix + "_ret := make([]" + t.RenderTypeGo() + ", int(" + namePrefix + "_ma.len))\n"
afterword += "_outCast := (*[0xffff]" + t.parameterTypeCgo() + ")(unsafe.Pointer(" + namePrefix + "_ma.data)) // mrs jackson\n"
afterword += "for i := 0; i < int(" + namePrefix + "_ma.len); i++ {\n"
if t.QtClassType() {
if !t.Pointer {
// new, but then dereference it
afterword += "" + namePrefix + "_ret[i] = *new" + cabiClassName(t.ParameterType) + "(_outCast[i])\n"
} else {
afterword += "" + namePrefix + "_ret[i] = new" + cabiClassName(t.ParameterType) + "(_outCast[i])\n"
}
} else { // plain int type
afterword += "" + namePrefix + "_ret[i] = (" + t.RenderTypeGo() + ")(_outCast[i])\n"
}
afterword += "}\n"
afterword += "C.free(unsafe.Pointer(" + namePrefix + "_ma))\n"
afterword += assignExpr + " " + namePrefix + "_ret\n"
}
afterword += "}\n"
afterword += "C.free(unsafe.Pointer(" + namePrefix + "_ma))\n"
afterword += assignExpr + " " + namePrefix + "_ret\n"
} else if rt.QtClassType() {
// Construct our Go type based on this inner CABI type
@ -347,7 +323,7 @@ func (gfs *goFileState) emitCabiToGo(assignExpr string, rt CppParameter, rvalue
if rt.Pointer || rt.ByRef {
gfs.imports["unsafe"] = struct{}{}
afterword = assignExpr + " new" + cabiClassName(rt.ParameterType) + "_U(unsafe.Pointer(" + namePrefix + "_ret))"
return assignExpr + " new" + cabiClassName(rt.ParameterType) + "_U(unsafe.Pointer(" + rvalue + "))"
} else {
// This is return by value, but CABI has new'd it into a
@ -371,8 +347,8 @@ func (gfs *goFileState) emitCabiToGo(assignExpr string, rt CppParameter, rvalue
} else if rt.IntType() || rt.ParameterType == "bool" {
// Need to cast Cgo type to Go int type
shouldReturn = "" + namePrefix + "_ret := "
afterword += assignExpr + "(" + rt.RenderTypeGo() + ")(" + namePrefix + "_ret)\n"
// Optimize assignment to avoid temporary
return assignExpr + "(" + rt.RenderTypeGo() + ")(" + rvalue + ")\n"
}