genbindings: QString parameter forwarding with custom preamble

This commit is contained in:
mappu 2024-08-08 18:54:13 +12:00
parent 618b68aaf9
commit 0c275047c3
3 changed files with 110 additions and 40 deletions

View File

@ -5,6 +5,14 @@ import (
"strings" "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 { func emitParametersCpp(params []CppParameter, selfType string) string {
tmp := make([]string, 0, len(params)+1) tmp := make([]string, 0, len(params)+1)
@ -13,12 +21,24 @@ func emitParametersCpp(params []CppParameter, selfType string) string {
} }
for _, p := range params { for _, p := range params {
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) tmp = append(tmp, p.RenderTypeCpp()+" "+p.ParameterName)
} }
}
return strings.Join(tmp, ", ") 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) tmp := make([]string, 0, len(params)+1)
if selfType != "" { if selfType != "" {
@ -26,9 +46,22 @@ func emitParametersNames(params []CppParameter, selfType string) string {
} }
for _, p := range params { for _, p := range params {
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) tmp = append(tmp, p.ParameterName)
} }
return strings.Join(tmp, ", ") }
return preamble, strings.Join(tmp, ", ")
} }
func emitBindingHeader(src *CppParsedHeader, filename string) (string, error) { 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 _, c := range src.Classes {
for i, ctor := range c.Ctors { for i, ctor := range c.Ctors {
preamble, forwarding := emitParametersCABI2CppForwarding(ctor.Parameters, "")
ret.WriteString(fmt.Sprintf( 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, ""), "P%s %s_new%s(%s) {\n"+
c.ClassName, emitParametersNames(ctor.Parameters, ""), "%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 = "" 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), preamble, forwarding := emitParametersCABI2CppForwarding(m.Parameters, c.ClassName)
shouldReturn, c.ClassName, m.MethodName, emitParametersNames(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,
)) ))
} }
} }

View File

@ -6,6 +6,22 @@ import (
"strings" "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 { func emitParametersGo(params []CppParameter) string {
tmp := make([]string, 0, len(params)) tmp := make([]string, 0, len(params))
for _, p := range params { for _, p := range params {
@ -14,6 +30,38 @@ func emitParametersGo(params []CppParameter) string {
return strings.Join(tmp, ", ") 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) { func emitGo(src *CppParsedHeader) (string, error) {
ret := strings.Builder{} ret := strings.Builder{}
@ -47,10 +95,11 @@ import "C"
`) `)
for i, ctor := range c.Ctors { for i, ctor := range c.Ctors {
preamble, forwarding := emitParametersGo2CABIForwarding(ctor.Parameters)
ret.WriteString(` ret.WriteString(`
// New` + c.ClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object. // New` + c.ClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object.
func New` + c.ClassName + maybeSuffix(i) + `(` + emitParametersGo(ctor.Parameters) + `) { 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} return &` + c.ClassName + `{h: ret}
} }
@ -67,9 +116,11 @@ import "C"
returnTypeDecl = "" returnTypeDecl = ""
} }
preamble, forwarding := emitParametersGo2CABIForwarding(m.Parameters)
ret.WriteString(` ret.WriteString(`
func (this *` + c.ClassName + `) ` + m.SafeMethodName() + `(` + emitParametersGo(m.Parameters) + `) ` + returnTypeDecl + ` { 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 + `)
} }
`) `)

View File

@ -12,36 +12,6 @@ type CppParameter struct {
ByRef bool 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 { type CppProperty struct {
PropertyName string PropertyName string
PropertyType string PropertyType string