diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index d769ea3..9dd2d47 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -78,15 +78,14 @@ func emitReturnTypeCabi(p CppParameter) string { return "struct miqt_string*" } else if _, ok := p.QListOf(); ok { - return "void" // Will be handled separately + return "struct miqt_array*" } else if (p.Pointer || p.ByRef) && p.QtClassType() { - return cabiClassName(p.ParameterType) + "*" // CABI type + return cabiClassName(p.ParameterType) + "*" } else if p.QtClassType() && !p.Pointer { // Even if C++ returns by value, CABI is returning a heap copy (new'd, not malloc'd) - return cabiClassName(p.ParameterType) + "*" // CABI type - // return "void" // Handled separately with an _out pointer + return cabiClassName(p.ParameterType) + "*" } else { return p.RenderTypeCabi() @@ -145,22 +144,7 @@ func emitParametersCabi(m CppMethod, selfType string) string { tmp = append(tmp, "struct miqt_string* "+p.ParameterName) } else if t, ok := p.QListOf(); ok { - - if t.ParameterType == "QString" { - // Combo - tmp = append(tmp, "char** "+p.ParameterName+", uint64_t* "+p.ParameterName+"_Lengths, size_t "+p.ParameterName+"_len") - - } else if t.QtClassType() && !t.Pointer { - // The Go code can only work with Qt types as pointers, so the CABI needs to take an array of - // pointers, not an array of values - // Needs one more level of indirection - tmp = append(tmp, t.RenderTypeCabi()+"** "+p.ParameterName+", size_t "+p.ParameterName+"_len") - - } else { - // The Go code has called this with two arguments: T* and len - // Declare that we take two parameters - tmp = append(tmp, t.RenderTypeCabi()+"* "+p.ParameterName+", size_t "+p.ParameterName+"_len") - } + tmp = append(tmp, "struct miqt_array* /* of "+t.RenderTypeCabi()+" */ "+p.ParameterName) } else if p.QtClassType() { if p.ByRef || p.Pointer { @@ -181,31 +165,6 @@ func emitParametersCabi(m CppMethod, selfType string) string { } } - // If the return type is QString, we need to handle returns via extra CABI - // parameters - // Qt C++: memory is in QString RAII - // CABI: memory is moved into C.malloc/C.free - // Go: converted to native Go string - if t, ok := m.ReturnType.QListOf(); ok { - // +1 pointer indirection since it's a heap array - // +1 pointer indirection for mutating remote parameter - // = 3 for char*, 2 for most types - - // Maybe: +1 pointer indirection if we have to lift stack types to the heap - - if t.ParameterType == "QString" { - // Combo - tmp = append(tmp, "char*** _out, int** _out_Lengths, size_t* _out_len") // Each length is a C.int for C.GoStringN use - } else if t.QtClassType() && !t.Pointer { - // QList QByteArray::Split() - // We need to pointer-ify each of the interior elements too - tmp = append(tmp, t.RenderTypeCabi()+"*** _out, size_t* _out_len") - } else { - tmp = append(tmp, t.RenderTypeCabi()+"** _out, size_t* _out_len") - } - - } - return strings.Join(tmp, ", ") } @@ -223,11 +182,14 @@ func emitParametersCABI2CppForwarding(params []CppParameter) (preamble string, f if listType.ParameterType == "QString" { + // miqt_array + // Combo (3 parameters) preamble += "\t" + p.ParameterType + " " + p.ParameterName + "_QList;\n" - preamble += "\t" + p.ParameterName + "_QList.reserve(" + p.ParameterName + "_len);\n" - preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "_len; ++i) {\n" - preamble += "\t\t" + p.ParameterName + "_QList.push_back(QString::fromUtf8(" + p.ParameterName + "[i], " + p.ParameterName + "_Lengths[i]));\n" + preamble += "\t" + p.ParameterName + "_QList.reserve(" + p.ParameterName + "->len);\n" + preamble += "\tmiqt_string** " + p.ParameterName + "_arr = static_cast(" + p.ParameterName + "->data);\n" + preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "->len; ++i) {\n" + preamble += "\t\t" + p.ParameterName + "_QList.push_back(QString::fromUtf8(& " + p.ParameterName + "_arr[i]->data, " + p.ParameterName + "_arr[i]->len));\n" preamble += "\t}\n" tmp = append(tmp, p.ParameterName+"_QList") @@ -236,14 +198,19 @@ func emitParametersCABI2CppForwarding(params []CppParameter) (preamble string, f // The CABI has accepted two parameters - need to convert to one real QList<> // Create it on the stack preamble += "\t" + p.ParameterType + " " + p.ParameterName + "_QList;\n" - preamble += "\t" + p.ParameterName + "_QList.reserve(" + p.ParameterName + "_len);\n" - preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "_len; ++i) {\n" + preamble += "\t" + p.ParameterName + "_QList.reserve(" + p.ParameterName + "->len);\n" if listType.QtClassType() && !listType.Pointer { - preamble += "\t\t" + p.ParameterName + "_QList.push_back(*(" + p.ParameterName + "[i]));\n" + preamble += "\t" + listType.PointerTo().RenderTypeCabi() + "* " + p.ParameterName + "_arr = static_cast<" + listType.PointerTo().RenderTypeCabi() + "*>(" + p.ParameterName + "->data);\n" + preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "->len; ++i) {\n" + preamble += "\t\t" + p.ParameterName + "_QList.push_back(*(" + p.ParameterName + "_arr[i]));\n" } else if listType.IsFlagType() { - preamble += "\t\t" + p.ParameterName + "_QList.push_back(static_cast<" + listType.RenderTypeQtCpp() + ">(" + p.ParameterName + "[i]));\n" + preamble += "\t" + listType.RenderTypeCabi() + "* " + p.ParameterName + "_arr = static_cast<" + listType.RenderTypeCabi() + "*>(" + p.ParameterName + "->data);\n" + preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "->len; ++i) {\n" + preamble += "\t\t" + p.ParameterName + "_QList.push_back(static_cast<" + listType.RenderTypeQtCpp() + ">(" + p.ParameterName + "_arr[i]));\n" } else { - preamble += "\t\t" + p.ParameterName + "_QList.push_back(" + p.ParameterName + "[i]);\n" + preamble += "\t" + listType.RenderTypeCabi() + "* " + p.ParameterName + "_arr = static_cast<" + listType.RenderTypeCabi() + "*>(" + p.ParameterName + "->data);\n" + preamble += "\tfor(size_t i = 0; i < " + p.ParameterName + "->len; ++i) {\n" + preamble += "\t\t" + p.ParameterName + "_QList.push_back(" + p.ParameterName + "_arr[i]);\n" } preamble += "\t}\n" tmp = append(tmp, p.ParameterName+"_QList") @@ -351,48 +318,55 @@ func emitAssignCppToCabi(assignExpression string, p CppParameter, rvalue string) // Combo // "char** _out, int64_t* _out_Lengths, size_t* _out_len") - shouldReturn = p.RenderTypeQtCpp() + " ret = " + shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = " + afterCall += indent + "// Convert QStringList from C++ memory to manually-managed C memory\n" - afterCall += indent + "char** __out = static_cast(malloc(sizeof(char*) * ret.length()));\n" - afterCall += indent + "int* __out_Lengths = static_cast(malloc(sizeof(int) * ret.length()));\n" - afterCall += indent + "for (size_t i = 0, e = ret.length(); i < e; ++i) {\n" - afterCall += indent + "\t// Convert QString from UTF-16 in C++ RAII memory to UTF-8 in manually-managed C memory\n" - afterCall += indent + "\tQByteArray b = ret[i].toUtf8();\n" - afterCall += indent + "\t__out[i] = static_cast(malloc(b.length()));\n" - afterCall += indent + "\tmemcpy(__out[i], b.data(), b.length());\n" - afterCall += indent + "\t__out_Lengths[i] = b.length();\n" + afterCall += indent + "struct miqt_string** " + namePrefix + "_arr = static_cast(malloc(sizeof(struct miqt_string*) * " + namePrefix + "_ret.length()));\n" + afterCall += indent + "for (size_t i = 0, e = " + namePrefix + "_ret.length(); i < e; ++i) {\n" + afterCall += emitAssignCppToCabi(indent+"\t"+namePrefix+"_arr[i] = ", t, namePrefix+"_ret[i]") afterCall += indent + "}\n" - afterCall += indent + "*_out = __out;\n" - afterCall += indent + "*_out_Lengths = __out_Lengths;\n" - afterCall += indent + "*_out_len = ret.length();\n" + + afterCall += indent + "struct miqt_array* " + namePrefix + "_out = static_cast(malloc(sizeof(struct miqt_array)));\n" + afterCall += indent + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n" + afterCall += indent + namePrefix + "_out->data = static_cast(" + namePrefix + "_arr);\n" + + afterCall += indent + assignExpression + "" + namePrefix + "_out;\n" } else if !t.QtClassType() || (t.QtClassType() && t.Pointer) { // QList, QList shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = " + afterCall += indent + "// Convert QList<> from C++ memory to manually-managed C memory\n" - afterCall += indent + "" + t.RenderTypeCabi() + "* __out = static_cast<" + t.RenderTypeCabi() + "*>(malloc(sizeof(" + t.RenderTypeCabi() + ") * " + namePrefix + "_ret.length()));\n" + afterCall += indent + "" + t.RenderTypeCabi() + "* " + namePrefix + "_arr = static_cast<" + t.RenderTypeCabi() + "*>(malloc(sizeof(" + t.RenderTypeCabi() + ") * " + namePrefix + "_ret.length()));\n" afterCall += indent + "for (size_t i = 0, e = " + namePrefix + "_ret.length(); i < e; ++i) {\n" if t.Const { - nonConst := t // copy - nonConst.Const = false - afterCall += indent + "\t__out[i] = const_cast<" + t.RenderTypeCabi() + ">(" + namePrefix + "_ret[i]);\n" + afterCall += indent + "\t" + namePrefix + "_arr[i] = const_cast<" + t.ConstCast(false).RenderTypeCabi() + ">(" + namePrefix + "_ret[i]);\n" } else { - afterCall += indent + "\t__out[i] = " + namePrefix + "_ret[i];\n" + afterCall += indent + "\t" + namePrefix + "_arr[i] = " + namePrefix + "_ret[i];\n" } afterCall += indent + "}\n" - afterCall += indent + "*_out = __out;\n" - afterCall += indent + "*_out_len = " + namePrefix + "_ret.length();\n" + + afterCall += indent + "struct miqt_array* " + namePrefix + "_out = static_cast(malloc(sizeof(struct miqt_array)));\n" + afterCall += indent + "" + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n" + afterCall += indent + "" + namePrefix + "_out->data = static_cast(" + namePrefix + "_arr);\n" + + afterCall += indent + assignExpression + "" + namePrefix + "_out;\n" } else { // QList shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = " + afterCall += indent + "// Convert QList<> from C++ memory to manually-managed C memory of copy-constructed pointers\n" - afterCall += indent + "" + t.RenderTypeCabi() + "** __out = static_cast<" + t.RenderTypeCabi() + "**>(malloc(sizeof(" + t.RenderTypeCabi() + "**) * " + namePrefix + "_ret.length()));\n" + afterCall += indent + "" + t.RenderTypeCabi() + "** " + namePrefix + "_arr = static_cast<" + t.RenderTypeCabi() + "**>(malloc(sizeof(" + t.RenderTypeCabi() + "**) * " + namePrefix + "_ret.length()));\n" afterCall += indent + "for (size_t i = 0, e = " + namePrefix + "_ret.length(); i < e; ++i) {\n" - afterCall += indent + "\t__out[i] = new " + t.ParameterType + "(" + namePrefix + "_ret[i]);\n" + afterCall += indent + "\t" + namePrefix + "_arr[i] = new " + t.ParameterType + "(" + namePrefix + "_ret[i]);\n" afterCall += indent + "}\n" - afterCall += indent + "*_out = __out;\n" - afterCall += indent + "*_out_len = " + namePrefix + "_ret.length();\n" + + afterCall += indent + "struct miqt_array* " + namePrefix + "_out = static_cast(malloc(sizeof(struct miqt_array)));\n" + afterCall += indent + "" + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n" + afterCall += indent + "" + namePrefix + "_out->data = static_cast(" + namePrefix + "_arr);\n" + + afterCall += indent + assignExpression + "" + namePrefix + "_out;\n" } @@ -414,7 +388,7 @@ func emitAssignCppToCabi(assignExpression string, p CppParameter, rvalue string) } else if p.QtClassType() && !p.Pointer { shouldReturn = p.ParameterType + " " + namePrefix + "_ret = " afterCall = indent + "// Copy-construct value returned type into heap-allocated copy\n" - afterCall += indent + "return static_cast<" + p.ParameterType + "*>(new " + p.ParameterType + "(" + namePrefix + "_ret));\n" + afterCall += indent + "" + assignExpression + "static_cast<" + p.ParameterType + "*>(new " + p.ParameterType + "(" + namePrefix + "_ret));\n" } else if p.Const { shouldReturn += "(" + emitReturnTypeCabi(p) + ") " diff --git a/cmd/genbindings/emitgo.go b/cmd/genbindings/emitgo.go index 72c0f72..ef7e6a2 100644 --- a/cmd/genbindings/emitgo.go +++ b/cmd/genbindings/emitgo.go @@ -187,7 +187,7 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s preamble += p.ParameterName + "_ms := miqt_strdupg(" + p.ParameterName + ")\n" preamble += "defer C.free(" + p.ParameterName + "_ms)\n" - tmp = append(tmp, p.ParameterName+"_ms") + tmp = append(tmp, "(*C.struct_miqt_string)("+p.ParameterName+"_ms)") } else if listType, ok := p.QListOf(); ok { // QList @@ -200,17 +200,18 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s 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 += p.ParameterName + "_Lengths := (*[0xffff]C.uint64_t)(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n" preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_CArray))\n" - preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_Lengths))\n" + preamble += "for i := range " + p.ParameterName + "{\n" - preamble += "single_cstring := C.CString(" + p.ParameterName + "[i])\n" - preamble += "defer C.free(unsafe.Pointer(single_cstring))\n" - preamble += p.ParameterName + "_CArray[i] = single_cstring\n" - preamble += p.ParameterName + "_Lengths[i] = (C.uint64_t)(len(" + p.ParameterName + "[i]))\n" + preamble += "single_ms := miqt_strdupg(" + p.ParameterName + "[i])\n" + preamble += "defer C.free(unsafe.Pointer(single_ms))\n" + preamble += p.ParameterName + "_CArray[i] = single_ms\n" preamble += "}\n" - tmp = append(tmp, "&"+p.ParameterName+"_CArray[0], &"+p.ParameterName+"_Lengths[0], C.size_t(len("+p.ParameterName+"))") + 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) } else { @@ -221,6 +222,7 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s 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" @@ -229,7 +231,10 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s } preamble += "}\n" - tmp = append(tmp, "&"+p.ParameterName+"_CArray[0], C.size_t(len("+p.ParameterName+"))") + 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) } } else if p.Pointer && p.ParameterType == "char" { @@ -258,16 +263,6 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s } } - if t, ok := m.ReturnType.QListOf(); ok { - - if t.ParameterType == "QString" { - // Combo - tmp = append(tmp, "&_out, &_out_Lengths, &_out_len") - } else { - tmp = append(tmp, "&_out, &_out_len") - } - } - return preamble, strings.Join(tmp, ", ") } @@ -425,38 +420,30 @@ import "C" } else if t, ok := m.ReturnType.QListOf(); ok { gfs.imports["unsafe"] = struct{}{} - shouldReturn = "" returnTypeDecl = "[]" + t.RenderTypeGo() + shouldReturn = "var ret_ma *C.struct_miqt_array = " if t.ParameterType == "QString" { // Combo - preamble += "var _out **C.char = nil\n" - preamble += "var _out_Lengths *C.int = nil\n" - preamble += "var _out_len C.size_t = 0\n" - afterword += "ret := make([]string, int(_out_len))\n" - afterword += "_outCast := (*[0xffff]*C.char)(unsafe.Pointer(_out)) // hey ya\n" - afterword += "_out_LengthsCast := (*[0xffff]C.int)(unsafe.Pointer(_out_Lengths))\n" - afterword += "for i := 0; i < int(_out_len); i++ {\n" - afterword += "ret[i] = C.GoStringN(_outCast[i], _out_LengthsCast[i])\n" + afterword += "ret := make([]string, int(ret_ma.len))\n" + afterword += "_outCast := (*[0xffff]*C.struct_miqt_string)(unsafe.Pointer(ret_ma.data)) // hey ya\n" + afterword += "for i := 0; i < int(ret_ma.len); i++ {\n" + afterword += "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(_out))\n" + afterword += "C.free(unsafe.Pointer(ret_ma))\n" afterword += "return ret\n" } else { + + afterword += "ret := make([]" + t.RenderTypeGo() + ", int(ret_ma.len))\n" if t.QtClassType() { - preamble += "var _out **" + t.parameterTypeCgo() + " = nil\n" + afterword += "_outCast := (*[0xffff]*" + t.parameterTypeCgo() + ")(unsafe.Pointer(ret_ma.data)) // so fresh so clean\n" } else { - preamble += "var _out *" + t.parameterTypeCgo() + " = nil\n" + afterword += "_outCast := (*[0xffff]" + t.parameterTypeCgo() + ")(unsafe.Pointer(ret_ma.data)) // mrs jackson\n" } - preamble += "var _out_len C.size_t = 0\n" - afterword += "ret := make([]" + t.RenderTypeGo() + ", int(_out_len))\n" - if t.QtClassType() { - afterword += "_outCast := (*[0xffff]*" + t.parameterTypeCgo() + ")(unsafe.Pointer(_out)) // so fresh so clean\n" - } else { - afterword += "_outCast := (*[0xffff]" + t.parameterTypeCgo() + ")(unsafe.Pointer(_out)) // mrs jackson\n" - } - afterword += "for i := 0; i < int(_out_len); i++ {\n" + afterword += "for i := 0; i < int(ret_ma.len); i++ {\n" if t.QtClassType() { if !t.Pointer { // new, but then dereference it @@ -468,7 +455,7 @@ import "C" afterword += "ret[i] = (" + t.RenderTypeGo() + ")(_outCast[i])\n" } afterword += "}\n" - afterword += "C.free(unsafe.Pointer(_out))\n" + afterword += "C.free(unsafe.Pointer(ret_ma))\n" afterword += "return ret\n" } diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index 73a58a0..15b8c42 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -59,6 +59,19 @@ func (p *CppParameter) CopyWithAlias(alias CppParameter) CppParameter { return ret } +func (p *CppParameter) PointerTo() CppParameter { + ret := *p // Copy + ret.Pointer = true + ret.PointerCount++ + return ret +} + +func (p *CppParameter) ConstCast(isConst bool) CppParameter { + ret := *p // Copy + ret.Const = isConst + return ret +} + func (p *CppParameter) UnderlyingType() string { if p.TypeAlias != "" { return p.TypeAlias @@ -120,11 +133,15 @@ func (p CppParameter) IsEnum() bool { func (p CppParameter) QListOf() (CppParameter, bool) { if strings.HasPrefix(p.ParameterType, "QList<") && strings.HasSuffix(p.ParameterType, `>`) { - return parseSingleTypeString(p.ParameterType[6 : len(p.ParameterType)-1]), true + ret := parseSingleTypeString(p.ParameterType[6 : len(p.ParameterType)-1]) + ret.ParameterName = p.ParameterName + "_lv" + return ret, true } if strings.HasPrefix(p.ParameterType, "QVector<") && strings.HasSuffix(p.ParameterType, `>`) { - return parseSingleTypeString(p.ParameterType[8 : len(p.ParameterType)-1]), true + ret := parseSingleTypeString(p.ParameterType[8 : len(p.ParameterType)-1]) + ret.ParameterName = p.ParameterName + "_vv" + return ret, true } return CppParameter{}, false diff --git a/qt/binding2.go b/qt/binding2.go index 34e086b..18bb70d 100644 --- a/qt/binding2.go +++ b/qt/binding2.go @@ -13,9 +13,13 @@ struct miqt_string* miqt_strdupg(_GoString_ gs) { */ import "C" +import ( + "unsafe" +) + // miqt_strdupg will strdup a Go string into a miqt_string*. // It is typed as returning an unsafe.Pointer because Cgo types cannot be shared // across Go file boundaries. func miqt_strdupg(s string) unsafe.Pointer { - return C.miqt_strdupg(s) + return unsafe.Pointer(C.miqt_strdupg(s)) }