genbindings: implement miqt_array parameter passing

This commit is contained in:
mappu 2024-09-14 18:26:59 +12:00
parent 82f8063df5
commit 04399db98d
4 changed files with 102 additions and 120 deletions

View File

@ -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> 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<miqt_string*>
// 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<miqt_string**>(" + 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<char**>(malloc(sizeof(char*) * ret.length()));\n"
afterCall += indent + "int* __out_Lengths = static_cast<int*>(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<char*>(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<struct miqt_string**>(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<struct miqt_array*>(malloc(sizeof(struct miqt_array)));\n"
afterCall += indent + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n"
afterCall += indent + namePrefix + "_out->data = static_cast<void*>(" + namePrefix + "_arr);\n"
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
} else if !t.QtClassType() || (t.QtClassType() && t.Pointer) { // QList<int>, QList<QFoo*>
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<struct miqt_array*>(malloc(sizeof(struct miqt_array)));\n"
afterCall += indent + "" + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n"
afterCall += indent + "" + namePrefix + "_out->data = static_cast<void*>(" + namePrefix + "_arr);\n"
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
} else { // QList<QFoo>
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<struct miqt_array*>(malloc(sizeof(struct miqt_array)));\n"
afterCall += indent + "" + namePrefix + "_out->len = " + namePrefix + "_ret.length();\n"
afterCall += indent + "" + namePrefix + "_out->data = static_cast<void*>(" + 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) + ") "

View File

@ -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<T>
@ -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"
}

View File

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

View File

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