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"
)
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,
))
}
}

View File

@ -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 + `)
}
`)

View File

@ -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