diff --git a/cmd/genbindings/config-allowlist.go b/cmd/genbindings/config-allowlist.go index 9486cb56..06123fc1 100644 --- a/cmd/genbindings/config-allowlist.go +++ b/cmd/genbindings/config-allowlist.go @@ -226,9 +226,6 @@ func AllowMethod(className string, mm CppMethod) error { // Any type not permitted by AllowClass is also not permitted by this method. func AllowType(p CppParameter, isReturnType bool) error { - if p.QPairOf() { - return ErrTooComplex // e.g. QGradientStop - } if t, ok := p.QSetOf(); ok { if err := AllowType(t, isReturnType); err != nil { return err @@ -258,6 +255,14 @@ func AllowType(p CppParameter, isReturnType bool) error { return ErrTooComplex } } + if kType, vType, ok := p.QPairOf(); ok { + if err := AllowType(kType, isReturnType); err != nil { + return err + } + if err := AllowType(vType, isReturnType); err != nil { + return err + } + } if p.QMultiMapOf() { return ErrTooComplex // e.g. Qt5 QNetwork qsslcertificate.h has a QMultiMap } diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index ec92b925..f0b15c62 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -23,6 +23,9 @@ func (p CppParameter) RenderTypeCabi() string { } else if _, _, ok := p.QMapOf(); ok { return "struct miqt_map" + } else if _, _, ok := p.QPairOf(); ok { + return "struct miqt_map" + } else if (p.Pointer || p.ByRef) && p.QtClassType() { return cabiClassName(p.ParameterType) + "*" @@ -267,6 +270,25 @@ func emitCABI2CppForwarding(p CppParameter, indent string) (preamble string, for preamble += indent + "}\n" return preamble, nameprefix + "_QMap" + } else if kType, vType, ok := p.QPairOf(); ok { + preamble += indent + p.GetQtCppType().ParameterType + " " + nameprefix + "_QPair;\n" + + preamble += indent + kType.RenderTypeCabi() + "* " + nameprefix + "_first_arr = static_cast<" + kType.RenderTypeCabi() + "*>(" + p.ParameterName + ".keys);\n" + preamble += indent + vType.RenderTypeCabi() + "* " + nameprefix + "_second_arr = static_cast<" + vType.RenderTypeCabi() + "*>(" + p.ParameterName + ".values);\n" + + kType.ParameterName = nameprefix + "_first_arr[0]" + addPreK, addFwdK := emitCABI2CppForwarding(kType, indent+"\t") + preamble += addPreK + + vType.ParameterName = nameprefix + "_second_arr[0]" + addPreV, addFwdV := emitCABI2CppForwarding(vType, indent+"\t") + preamble += addPreV + + preamble += indent + nameprefix + "_QPair.first = " + addFwdK + ";\n" + preamble += indent + nameprefix + "_QPair.second = " + addFwdV + ";\n" + + return preamble, nameprefix + "_QPair" + } else if p.IsFlagType() || p.IntType() || p.IsKnownEnum() { castSrc := p.ParameterName castType := p.RenderTypeQtCpp() @@ -452,6 +474,26 @@ func emitAssignCppToCabi(assignExpression string, p CppParameter, rvalue string) afterCall += indent + assignExpression + "" + namePrefix + "_out;\n" return indent + shouldReturn + rvalue + ";\n" + afterCall + } else if kType, vType, ok := p.QPairOf(); ok { + // QPair + + shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = " + + afterCall += indent + "// Convert QPair<> from C++ memory to manually-managed C memory\n" + afterCall += indent + "" + kType.RenderTypeCabi() + "* " + namePrefix + "_first_arr = static_cast<" + kType.RenderTypeCabi() + "*>(malloc(sizeof(" + kType.RenderTypeCabi() + ")));\n" + afterCall += indent + "" + vType.RenderTypeCabi() + "* " + namePrefix + "_second_arr = static_cast<" + vType.RenderTypeCabi() + "*>(malloc(sizeof(" + vType.RenderTypeCabi() + ")));\n" + + afterCall += emitAssignCppToCabi(indent+namePrefix+"_first_arr[0] = ", kType, namePrefix+"_ret.first") + afterCall += emitAssignCppToCabi(indent+namePrefix+"_second_arr[0] = ", vType, namePrefix+"_ret.second") + + afterCall += indent + "struct miqt_map " + namePrefix + "_out;\n" + afterCall += indent + "" + namePrefix + "_out.len = 1;\n" + afterCall += indent + "" + namePrefix + "_out.keys = static_cast(" + namePrefix + "_first_arr);\n" + afterCall += indent + "" + namePrefix + "_out.values = static_cast(" + namePrefix + "_second_arr);\n" + + afterCall += indent + assignExpression + "" + namePrefix + "_out;\n" + return indent + shouldReturn + rvalue + ";\n" + afterCall + } else if p.QtClassType() && p.ByRef { // It's a pointer in disguise, just needs one cast shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = " diff --git a/cmd/genbindings/emitgo.go b/cmd/genbindings/emitgo.go index a7833d57..37861fc8 100644 --- a/cmd/genbindings/emitgo.go +++ b/cmd/genbindings/emitgo.go @@ -43,6 +43,12 @@ func (p CppParameter) RenderTypeGo(gfs *goFileState) string { return "map[" + t1.RenderTypeGo(gfs) + "]" + t2.RenderTypeGo(gfs) } + if t1, t2, ok := p.QPairOf(); ok { + // Design QPair using capital-named members, in case it gets passed + // across packages + return "struct { First " + t1.RenderTypeGo(gfs) + " ; Second " + t2.RenderTypeGo(gfs) + " }" + } + if p.ParameterType == "void" && p.Pointer { return "unsafe.Pointer" } @@ -169,6 +175,10 @@ func (p CppParameter) parameterTypeCgo() string { return "C.struct_miqt_map" } + if _, _, ok := p.QPairOf(); ok { + return "C.struct_miqt_map" + } + // Cgo internally binds void* as unsafe.Pointer{} if p.ParameterType == "void" && p.Pointer { return "unsafe.Pointer" @@ -366,6 +376,30 @@ func (gfs *goFileState) emitParameterGo2CABIForwarding(p CppParameter) (preamble rvalue = nameprefix + "_mm" + } else if kType, vType, ok := p.QPairOf(); ok { + // QPair + + gfs.imports["unsafe"] = struct{}{} + + preamble += nameprefix + "_First_CArray := (*[0xffff]" + kType.parameterTypeCgo() + ")(C.malloc(C.size_t(" + kType.mallocSizeCgoExpression() + ")))\n" + preamble += "defer C.free(unsafe.Pointer(" + nameprefix + "_First_CArray))\n" + + preamble += nameprefix + "_Second_CArray := (*[0xffff]" + vType.parameterTypeCgo() + ")(C.malloc(C.size_t(" + vType.mallocSizeCgoExpression() + ")))\n" + preamble += "defer C.free(unsafe.Pointer(" + nameprefix + "_Second_CArray))\n" + + kType.ParameterName = p.ParameterName + ".First" + addPreamble, innerRvalue := gfs.emitParameterGo2CABIForwarding(kType) + preamble += addPreamble + preamble += nameprefix + "_First_CArray[0] = " + innerRvalue + "\n" + + vType.ParameterName = p.ParameterName + ".Second" + addPreamble, innerRvalue = gfs.emitParameterGo2CABIForwarding(vType) + preamble += addPreamble + preamble += nameprefix + "_Second_CArray[0] = " + innerRvalue + "\n" + + preamble += nameprefix + "_pair := C.struct_miqt_map{\nlen: 1,\nkeys: unsafe.Pointer(" + nameprefix + "_First_CArray),\nvalues: unsafe.Pointer(" + nameprefix + "_Second_CArray),\n}\n" + + rvalue = nameprefix + "_pair" } else if p.Pointer && p.ParameterType == "char" { // Single char* argument @@ -506,6 +540,20 @@ func (gfs *goFileState) emitCabiToGo(assignExpr string, rt CppParameter, rvalue afterword += assignExpr + " " + namePrefix + "_ret\n" return shouldReturn + " " + rvalue + "\n" + afterword + } else if kType, vType, ok := rt.QPairOf(); ok { + gfs.imports["unsafe"] = struct{}{} + + shouldReturn = "var " + namePrefix + "_mm C.struct_miqt_map = " + + afterword += namePrefix + "_First_CArray := (*[0xffff]" + kType.parameterTypeCgo() + ")(unsafe.Pointer(" + namePrefix + "_mm.keys))\n" + afterword += namePrefix + "_Second_CArray := (*[0xffff]" + vType.parameterTypeCgo() + ")(unsafe.Pointer(" + namePrefix + "_mm.values))\n" + + afterword += gfs.emitCabiToGo(namePrefix+"_entry_First := ", kType, namePrefix+"_First_CArray[0]") + "\n" + afterword += gfs.emitCabiToGo(namePrefix+"_entry_Second := ", vType, namePrefix+"_Second_CArray[0]") + "\n" + + afterword += assignExpr + " " + rt.RenderTypeGo(gfs) + " { First: " + namePrefix + "_entry_First , Second: " + namePrefix + "_entry_Second }\n" + return shouldReturn + " " + rvalue + "\n" + afterword + } else if rt.QtClassType() { // Construct our Go type based on this inner CABI type shouldReturn = "" + namePrefix + "_ret := " diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index 9d9bac66..b9d7b5c9 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -151,8 +151,21 @@ func (p CppParameter) QMapOf() (CppParameter, CppParameter, bool) { return CppParameter{}, CppParameter{}, false } -func (p CppParameter) QPairOf() bool { - return strings.HasPrefix(p.ParameterType, `QPair<`) // TODO support this +func (p CppParameter) QPairOf() (CppParameter, CppParameter, bool) { + if strings.HasPrefix(p.ParameterType, `QPair<`) && strings.HasSuffix(p.ParameterType, `>`) { + interior := tokenizeMultipleParameters(p.ParameterType[6 : len(p.ParameterType)-1]) + if len(interior) != 2 { + panic("QPair<> has unexpected number of template arguments") + } + + first := parseSingleTypeString(interior[0]) + first.ParameterName = p.ParameterName + "_first" + second := parseSingleTypeString(interior[1]) + second.ParameterName = p.ParameterName + "_second" + return first, second, true + } + + return CppParameter{}, CppParameter{}, false } func (p CppParameter) QSetOf() (CppParameter, bool) { diff --git a/cmd/genbindings/transformtypedefs.go b/cmd/genbindings/transformtypedefs.go index b51ff170..e65d2e9f 100644 --- a/cmd/genbindings/transformtypedefs.go +++ b/cmd/genbindings/transformtypedefs.go @@ -44,6 +44,20 @@ func applyTypedefs(p CppParameter) CppParameter { p.QtCppOriginalType = &tmp } p.ParameterType = p.ParameterType[0:bpos] + `<` + kType2.RenderTypeQtCpp() + `, ` + vType2.RenderTypeQtCpp() + `>` + + } else if kType, vType, ok := p.QPairOf(); ok { + kType2 := applyTypedefs(kType) + kType2.QtCppOriginalType = nil + + vType2 := applyTypedefs(vType) + vType2.QtCppOriginalType = nil + + if p.QtCppOriginalType == nil { + tmp := p // copy + p.QtCppOriginalType = &tmp + } + p.ParameterType = `QPair<` + kType2.RenderTypeQtCpp() + `, ` + vType2.RenderTypeQtCpp() + `>` + } return p