From 0c275047c3d3df39c71021cc2f8d0051006797d3 Mon Sep 17 00:00:00 2001 From: mappu Date: Thu, 8 Aug 2024 18:54:13 +1200 Subject: [PATCH] genbindings: QString parameter forwarding with custom preamble --- cmd/genbindings/emitcabi.go | 65 +++++++++++++++++++++++++++++---- cmd/genbindings/emitgo.go | 55 +++++++++++++++++++++++++++- cmd/genbindings/intermediate.go | 30 --------------- 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index 1844e4c1..9ec4eb89 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -5,6 +5,14 @@ import ( "strings" ) +func (p CppParameter) RenderTypeCpp() string { + ret := p.ParameterType + if p.Pointer || p.ByRef { + ret += "*" + } + return ret // ignore const +} + func emitParametersCpp(params []CppParameter, selfType string) string { tmp := make([]string, 0, len(params)+1) @@ -13,12 +21,24 @@ func emitParametersCpp(params []CppParameter, selfType string) string { } for _, p := range params { - tmp = append(tmp, p.RenderTypeCpp()+" "+p.ParameterName) + if p.ParameterType == "QString" { + // The Go code has called this with two arguments: char* and len + // Declare that we take two parameters + tmp = append(tmp, "const char* "+p.ParameterName+", size_t "+p.ParameterName+"_Strlen") + + } else if (p.ByRef || p.Pointer) && p.ParameterType[0] == 'Q' { + // Pointer to Qt type + // Replace with taking our PQ typedef by value + tmp = append(tmp, "P"+p.ParameterType+" "+p.ParameterName) + } else { + // RenderTypeCpp renders both pointer+reference as pointers + tmp = append(tmp, p.RenderTypeCpp()+" "+p.ParameterName) + } } return strings.Join(tmp, ", ") } -func emitParametersNames(params []CppParameter, selfType string) string { +func emitParametersCABI2CppForwarding(params []CppParameter, selfType string) (preamble string, forwarding string) { tmp := make([]string, 0, len(params)+1) if selfType != "" { @@ -26,9 +46,22 @@ func emitParametersNames(params []CppParameter, selfType string) string { } for _, p := range params { - tmp = append(tmp, p.ParameterName) + if p.ParameterType == "QString" { + // The CABI has accepted two parameters - need to convert to one real QString + // Create it on the stack + preamble += "\tQString " + p.ParameterName + "_QString(" + p.ParameterName + ", " + p.ParameterName + "_Strlen);\n" + tmp = append(tmp, p.ParameterName+"_QString") + + } else if p.ByRef { + // We changed RenderTypeCpp() to render this as a pointer + // Need to dereference so we can pass as reference to the actual Qt C++ function + tmp = append(tmp, "*"+p.ParameterName) + } else { + tmp = append(tmp, p.ParameterName) + } } - return strings.Join(tmp, ", ") + + return preamble, strings.Join(tmp, ", ") } func emitBindingHeader(src *CppParsedHeader, filename string) (string, error) { @@ -83,9 +116,16 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { for _, c := range src.Classes { for i, ctor := range c.Ctors { + preamble, forwarding := emitParametersCABI2CppForwarding(ctor.Parameters, "") ret.WriteString(fmt.Sprintf( - "P%s %s_new%s(%s) {\n\treturn new %s(%s);\n}\n\n", c.ClassName, maybeSuffix(i), emitParametersCpp(ctor.Parameters, ""), - c.ClassName, emitParametersNames(ctor.Parameters, ""), + "P%s %s_new%s(%s) {\n"+ + "%s"+ + "\treturn new %s(%s);\n"+ + "}\n"+ + "\n", + c.ClassName, maybeSuffix(i), emitParametersCpp(ctor.Parameters, ""), + preamble, + c.ClassName, forwarding, )) } @@ -97,8 +137,17 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { shouldReturn = "" } - ret.WriteString(fmt.Sprintf("%s %s_%s(%s) {\n\t%sstatic_cast<%s*>(self)->%s(%s);\n}\n\n", m.ReturnType.RenderTypeCpp(), c.ClassName, m.SafeMethodName(), emitParametersCpp(m.Parameters, "P"+c.ClassName), - shouldReturn, c.ClassName, m.MethodName, emitParametersNames(m.Parameters, c.ClassName), + preamble, forwarding := emitParametersCABI2CppForwarding(m.Parameters, c.ClassName) + + ret.WriteString(fmt.Sprintf( + "%s %s_%s(%s) {\n"+ + "%s"+ + "\t%sstatic_cast<%s*>(self)->%s(%s);\n"+ + "}\n"+ + "\n", + m.ReturnType.RenderTypeCpp(), c.ClassName, m.SafeMethodName(), emitParametersCpp(m.Parameters, "P"+c.ClassName), + preamble, + shouldReturn, c.ClassName, m.MethodName, forwarding, )) } } diff --git a/cmd/genbindings/emitgo.go b/cmd/genbindings/emitgo.go index 95cb6909..88682b07 100644 --- a/cmd/genbindings/emitgo.go +++ b/cmd/genbindings/emitgo.go @@ -6,6 +6,22 @@ import ( "strings" ) +func (p CppParameter) RenderTypeGo() string { + if p.Pointer && p.ParameterType == "char" { + return "string" + } + if p.ParameterType == "QString" { + return "string" + } + + ret := "" + if p.ByRef || p.Pointer { + ret += "*" + } + ret += p.ParameterType + return ret // ignore const +} + func emitParametersGo(params []CppParameter) string { tmp := make([]string, 0, len(params)) for _, p := range params { @@ -14,6 +30,38 @@ func emitParametersGo(params []CppParameter) string { return strings.Join(tmp, ", ") } +func emitParametersGo2CABIForwarding(params []CppParameter) (preamble string, fowarding string) { + tmp := make([]string, 0, len(params)) + for _, p := range params { + if p.ParameterType == "QString" { + // Go: convert string -> char* and len + // CABI: convert char* and len -> real QString + preamble += p.ParameterName + "_Cstring := C.CString(" + p.ParameterName + ")\n" + preamble += "defer C.free(" + p.ParameterName + "_Cstring)\n" + tmp = append(tmp, p.ParameterName+"_Cstring, len("+p.ParameterName+")") + + // TODO handle the return type as a pointer parameter + + } else if p.Pointer && p.ParameterType == "char" { + // Single char* argument + preamble += p.ParameterName + "_Cstring := C.CString(" + p.ParameterName + ")\n" + preamble += "defer C.free(" + p.ParameterName + "_Cstring)\n" + tmp = append(tmp, p.ParameterName+"_Cstring") + + } else if (p.Pointer || p.ByRef) && p.ParameterType[0] == 'Q' { + // 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 { + // Default + tmp = append(tmp, p.ParameterName) + } + } + + return preamble, strings.Join(tmp, ", ") +} + func emitGo(src *CppParsedHeader) (string, error) { ret := strings.Builder{} @@ -47,10 +95,11 @@ import "C" `) for i, ctor := range c.Ctors { + preamble, forwarding := emitParametersGo2CABIForwarding(ctor.Parameters) ret.WriteString(` // New` + c.ClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object. func New` + c.ClassName + maybeSuffix(i) + `(` + emitParametersGo(ctor.Parameters) + `) { - ret := C.` + c.ClassName + `_new` + maybeSuffix(i) + `(` + emitParametersNames(ctor.Parameters, "") + `) + ` + preamble + ` ret := C.` + c.ClassName + `_new` + maybeSuffix(i) + `(` + forwarding + `) return &` + c.ClassName + `{h: ret} } @@ -67,9 +116,11 @@ import "C" returnTypeDecl = "" } + preamble, forwarding := emitParametersGo2CABIForwarding(m.Parameters) + ret.WriteString(` func (this *` + c.ClassName + `) ` + m.SafeMethodName() + `(` + emitParametersGo(m.Parameters) + `) ` + returnTypeDecl + ` { - ` + shouldReturn + ` C.` + c.ClassName + `_` + m.SafeMethodName() + `(` + emitParametersNames(m.Parameters, c.ClassName) + `) + ` + preamble + shouldReturn + ` C.` + c.ClassName + `_` + m.SafeMethodName() + `(` + forwarding + `) } `) diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index e3f7d620..a32366fb 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -12,36 +12,6 @@ type CppParameter struct { ByRef bool } -func (p CppParameter) RenderTypeCpp() string { - ret := "" - if p.ByRef { - ret += "&" - } - ret += p.ParameterType - if p.Pointer { - ret += "*" - } - return ret // ignore const -} - -func (p CppParameter) RenderTypeGo() string { - if p.Pointer && p.ParameterType == "char" { - return "string" - } - - ret := "" - if p.ByRef || p.Pointer { - /* - if p.ParameterType[0] == 'Q' { - ret += "C.P" // use our void typedef instead - } else { - */ - ret += "*" - } - ret += p.ParameterType - return ret // ignore const -} - type CppProperty struct { PropertyName string PropertyType string