diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index 39521478..1af8ff5a 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -18,6 +18,9 @@ func emitReturnTypeCabi(p CppParameter) string { if p.ParameterType == "QString" { return "void" // Will be handled separately + } else if _, ok := p.QListOf(); ok { + return "void" // Will be handled separately + } else if (p.Pointer || p.ByRef) && p.QtClassType() { return "P" + p.ParameterType // CABI type @@ -44,6 +47,12 @@ func emitParametersCabi(m CppMethod, selfType string) string { // Declare that we take two parameters tmp = append(tmp, "const char* "+p.ParameterName+", size_t "+p.ParameterName+"_Strlen") + } else if t, ok := p.QListOf(); ok { + // The Go code has called this with two arguments: T* and len + // Declare that we take two parameters + // TODO support QList + tmp = append(tmp, "P"+t.ParameterType+" "+p.ParameterName+", size_t "+p.ParameterName+"_len") + } else if (p.ByRef || p.Pointer) && p.QtClassType() { // Pointer to Qt type // Replace with taking our PQ typedef by value @@ -68,6 +77,10 @@ func emitParametersCabi(m CppMethod, selfType string) string { // Go: converted to native Go string if m.ReturnType.ParameterType == "QString" { tmp = append(tmp, "char** _out, size_t* _out_Strlen") + + } else if t, ok := m.ReturnType.QListOf(); ok { + tmp = append(tmp, "P"+t.ParameterType+"* _out, size_t* _out_len") + } return strings.Join(tmp, ", ") @@ -83,6 +96,16 @@ func emitParametersCABI2CppForwarding(params []CppParameter) (preamble string, f preamble += "\tQString " + p.ParameterName + "_QString = QString::fromUtf8(" + p.ParameterName + ", " + p.ParameterName + "_Strlen);\n" tmp = append(tmp, p.ParameterName+"_QString") + } else if _, ok := p.QListOf(); ok { + // The CABI has accepted two parameters - need to convert to one real QList<> + // Create it on the stack + preamble += "\t" + p.RenderTypeCpp() + " " + 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(" + p.ParameterName + "[i]);\n" + preamble += "\t}\n" + tmp = append(tmp, p.ParameterName+"_QList") + } 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 @@ -155,6 +178,10 @@ extern "C" { } sort.Strings(foundTypesList) for _, ft := range foundTypesList { + if strings.HasPrefix(ft, "QList<") { + continue + } + ret.WriteString(`typedef void* P` + ft + ";\n") } @@ -227,6 +254,15 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { afterCall += "\tmemcpy(*_out, b.data(), b.length());\n" afterCall += "\t*_out_Strlen = b.length();\n" + } else if t, ok := m.ReturnType.QListOf(); ok { + shouldReturn = m.ReturnType.ParameterType + " ret = " + afterCall += "\t// Convert QList<> from C++ memory to manually-managed C memory\n" + afterCall += "\t*_out = malloc(sizeof(" + t.RenderTypeCpp() + ") * ret.length());\n" + afterCall += "\tfor (int i = 0, e = ret.length(); i < e; ++i) {\n" + afterCall += "\t\t(*_out)[i] = ret[i];\n" + afterCall += "\t}\n" + afterCall += "\t*_out_len = ret.length()\n" + } else if m.ReturnType.QtClassType() && !m.ReturnType.Pointer { shouldReturn = m.ReturnType.ParameterType + " ret = " // afterCall = "\t// Copy-construct value returned type into Go-allocated POCO\n" diff --git a/cmd/genbindings/emitgo.go b/cmd/genbindings/emitgo.go index 78492586..c99c7b70 100644 --- a/cmd/genbindings/emitgo.go +++ b/cmd/genbindings/emitgo.go @@ -15,6 +15,10 @@ func (p CppParameter) RenderTypeGo() string { return "string" } + if t, ok := p.QListOf(); ok { + return "[]" + t.RenderTypeGo() + } + ret := "" if p.ByRef || p.Pointer { ret += "*" @@ -44,6 +48,22 @@ func emitParametersGo2CABIForwarding(m CppMethod) (preamble string, fowarding st preamble += "defer C.free(" + p.ParameterName + "_Cstring)\n" tmp = append(tmp, p.ParameterName+"_Cstring, len("+p.ParameterName+")") + } else if listType, ok := p.QListOf(); ok { + // QList + // Go: convert T[] -> t* and len + // CABI: create a real QList<> + + // TODO handle QList + + preamble += "// For the C ABI, malloc a C array of raw pointers\n" + preamble += p.ParameterName + "_CArray := (*[0xffff]C.P" + listType.ParameterType + ")(C.malloc(c.ulong(8 * len(" + p.ParameterName + "))))\n" + preamble += "defer C.free(" + p.ParameterName + "_CArray)\n" + preamble += "for i := range " + p.ParameterName + "{\n" + preamble += p.ParameterName + "_CArray[i] = " + p.ParameterName + "[i].cPointer()\n" + preamble += "}\n" + + tmp = append(tmp, p.ParameterName+"_CArray, len("+p.ParameterName+")") + } else if p.Pointer && p.ParameterType == "char" { // Single char* argument preamble += p.ParameterName + "_Cstring := C.CString(" + p.ParameterName + ")\n" @@ -63,6 +83,8 @@ func emitParametersGo2CABIForwarding(m CppMethod) (preamble string, fowarding st if m.ReturnType.ParameterType == "QString" { tmp = append(tmp, "&_out, &_out_Strlen") + } else if _, ok := m.ReturnType.QListOf(); ok { + tmp = append(tmp, "&_out, &_out_len") } return preamble, strings.Join(tmp, ", ") @@ -160,6 +182,18 @@ import "C" afterword += "C.free(_out)\n" afterword += "return ret" + } else if t, ok := m.ReturnType.QListOf(); ok { + shouldReturn = "" + returnTypeDecl = "[]" + t.RenderTypeGo() + + preamble += "var _out *C.P" + t.ParameterType + " = nil\n" + preamble += "var _out_len C.size_t = 0\n" + afterword += "ret := make([]" + t.RenderTypeGo() + ", _out_Strlen)\n" + afterword += "for i := 0; i < _out_len; i++ {\n" + afterword += "ret[i] = new" + t.ParameterType + "(_out[i])\n" + afterword += "}\n" + afterword += "C.free(_out)\n" + } else if m.ReturnType.QtClassType() { // Construct our Go type based on this inner CABI type shouldReturn = "ret := " diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index 1ac2adcd..eecc7fbf 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -17,6 +17,14 @@ func (p CppParameter) QtClassType() bool { return p.ParameterType[0] == 'Q' } +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 + } + + return CppParameter{}, false +} + type CppProperty struct { PropertyName string PropertyType string