From 15f4c367d044ba21c39e9ebbf5cfc5035bf37805 Mon Sep 17 00:00:00 2001 From: mappu Date: Tue, 17 Sep 2024 19:30:06 +1200 Subject: [PATCH] genbindings/go: refactor to reuse template emitters --- cmd/genbindings/emitgo.go | 190 +++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 107 deletions(-) diff --git a/cmd/genbindings/emitgo.go b/cmd/genbindings/emitgo.go index af41403..e551f97 100644 --- a/cmd/genbindings/emitgo.go +++ b/cmd/genbindings/emitgo.go @@ -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 - // 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 + // 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" }