From aaa9340808495a9acb034265b0752b955d891db1 Mon Sep 17 00:00:00 2001 From: mappu Date: Sat, 10 Aug 2024 12:52:45 +1200 Subject: [PATCH] genbindings: basic constructor support --- cmd/genbindings/clang2il.go | 159 +++++++++++++++++++++--------------- cmd/genbindings/emitcabi.go | 8 +- 2 files changed, 95 insertions(+), 72 deletions(-) diff --git a/cmd/genbindings/clang2il.go b/cmd/genbindings/clang2il.go index 0d31e2f4..41685022 100644 --- a/cmd/genbindings/clang2il.go +++ b/cmd/genbindings/clang2il.go @@ -98,10 +98,26 @@ nextMethod: // Safe to ignore case "CXXConstructorDecl": - // panic("TODO") + + + var mm CppMethod + err := parseMethod(node, &mm) + if err != nil { + if errors.Is(err, ErrTooComplex) { + log.Printf("Skipping method %q with complex type", mm.MethodName) + continue nextMethod + } + + // Real error + return CppClass{}, err + } + + + ret.Ctors = append(ret.Ctors, mm) case "CXXDestructorDecl": - // panic("TODO") + // We don't need to expose destructors in the binding beyond offering + // a regular delete function case "CXXMethodDecl": if !visibility { @@ -121,72 +137,15 @@ nextMethod: mm.MethodName = methodName[3:] } - if typobj, ok := node["type"].(map[string]interface{}); ok { - if qualType, ok := typobj["qualType"].(string); ok { - // The qualType is the whole type of the method, including its parameter types - // If anything here is too complicated, skip the whole method - - var err error = nil - mm.ReturnType, mm.Parameters, err = parseTypeString(qualType) - if err != nil { - if errors.Is(err, ErrTooComplex) { - log.Printf("Skipping method %q with complex type %q", mm.MethodName, qualType) - continue nextMethod - } - // Real error - return CppClass{}, err - } - + err := parseMethod(node, &mm) + if err != nil { + if errors.Is(err, ErrTooComplex) { + log.Printf("Skipping method %q with complex type", mm.MethodName) + continue nextMethod } - } - if methodInner, ok := node["inner"].([]interface{}); ok { - paramCounter := 0 - for _, methodObj := range methodInner { - methodObj, ok := methodObj.(map[string]interface{}) - if !ok { - return CppClass{}, errors.New("inner[] element not an object") - } - - switch methodObj["kind"] { - case "ParmVarDecl": - // Parameter variable - parmName, _ := methodObj["name"].(string) // n.b. may be unnamed - if parmName == "" { - - // Generate a default parameter name - // Super nice autogen names if this is a Q_PROPERTY setter: - if len(mm.Parameters) == 1 && strings.HasPrefix(mm.MethodName, "set") { - parmName = strings.ToLower(string(mm.MethodName[3])) + mm.MethodName[4:] - - } else { - // Otherwise - default - parmName = fmt.Sprintf("param%d", paramCounter+1) - } - } - - // Block reserved Go words, replace with generic parameters - if parmName == "default" || parmName == "const" || parmName == "func" { - parmName += "Val" - } - - // Update the name for the existing nth parameter - mm.Parameters[paramCounter].ParameterName = parmName - - // If this parameter has any internal AST nodes of its - // own, assume it means it's an optional parameter - if _, ok := methodObj["inner"]; ok { - mm.Parameters[paramCounter].Optional = true - } - - // Next - paramCounter++ - - default: - // Something else inside a declaration?? - fmt.Printf("==> NOT IMPLEMENTED CXXMethodDecl->%q\n", kind) - } - } + // Real error + return CppClass{}, err } ret.Methods = append(ret.Methods, mm) @@ -201,6 +160,74 @@ nextMethod: var ErrTooComplex error = errors.New("Type declaration is too complex to parse") +func parseMethod(node map[string]interface{}, mm *CppMethod) error { + + if typobj, ok := node["type"].(map[string]interface{}); ok { + if qualType, ok := typobj["qualType"].(string); ok { + // The qualType is the whole type of the method, including its parameter types + // If anything here is too complicated, skip the whole method + + var err error = nil + mm.ReturnType, mm.Parameters, err = parseTypeString(qualType) + if err != nil { + return err + } + + } + } + + if methodInner, ok := node["inner"].([]interface{}); ok { + paramCounter := 0 + for _, methodObj := range methodInner { + methodObj, ok := methodObj.(map[string]interface{}) + if !ok { + return errors.New("inner[] element not an object") + } + + switch methodObj["kind"] { + case "ParmVarDecl": + // Parameter variable + parmName, _ := methodObj["name"].(string) // n.b. may be unnamed + if parmName == "" { + + // Generate a default parameter name + // Super nice autogen names if this is a Q_PROPERTY setter: + if len(mm.Parameters) == 1 && strings.HasPrefix(mm.MethodName, "set") { + parmName = strings.ToLower(string(mm.MethodName[3])) + mm.MethodName[4:] + + } else { + // Otherwise - default + parmName = fmt.Sprintf("param%d", paramCounter+1) + } + } + + // Block reserved Go words, replace with generic parameters + if parmName == "default" || parmName == "const" || parmName == "func" { + parmName += "Val" + } + + // Update the name for the existing nth parameter + mm.Parameters[paramCounter].ParameterName = parmName + + // If this parameter has any internal AST nodes of its + // own, assume it means it's an optional parameter + if _, ok := methodObj["inner"]; ok { + mm.Parameters[paramCounter].Optional = true + } + + // Next + paramCounter++ + + default: + // Something else inside a declaration?? + fmt.Printf("==> NOT IMPLEMENTED CXXMethodDecl->%q\n", methodObj["kind"]) + } + } + } + + return nil +} + // parseTypeString converts a string like // - `QString (const char *, const char *, int)` // - `void (const QKeySequence \u0026)` diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index c37378f0..39521478 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -68,10 +68,6 @@ 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 m.ReturnType.QtClassType() && !m.ReturnType.Pointer { - tmp = append(tmp, "P"+m.ReturnType.ParameterType+" _out") - */ } return strings.Join(tmp, ", ") @@ -167,7 +163,7 @@ extern "C" { for _, c := range src.Classes { for i, ctor := range c.Ctors { - ret.WriteString(fmt.Sprintf("P%s %s_new%s(%s);\n", c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, ""))) + ret.WriteString(fmt.Sprintf("P%s %s_new%s(%s);\n", c.ClassName, c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, ""))) } for _, m := range c.Methods { @@ -208,7 +204,7 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "\treturn new %s(%s);\n"+ "}\n"+ "\n", - c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, ""), + c.ClassName, c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, ""), preamble, c.ClassName, forwarding, ))