diff --git a/cmd/genbindings/clang2il.go b/cmd/genbindings/clang2il.go index aa5d0e47..628ca375 100644 --- a/cmd/genbindings/clang2il.go +++ b/cmd/genbindings/clang2il.go @@ -460,6 +460,14 @@ nextMethod: } mm.IsSignal = isSignal && !mm.IsStatic && AllowSignal(mm) + mm.IsProtected = (visibility == VsProtected) + + if mm.IsProtected && !mm.IsVirtual { + // Protected method, so we can't call it + // Non-virtual, so we can't override it + // There is nothing we can do with this function + continue nextMethod + } // Once all processing is complete, pass to exceptions for final decision diff --git a/cmd/genbindings/config-allowlist.go b/cmd/genbindings/config-allowlist.go index 3ae2dda8..b874768b 100644 --- a/cmd/genbindings/config-allowlist.go +++ b/cmd/genbindings/config-allowlist.go @@ -186,6 +186,10 @@ func AllowSignal(mm CppMethod) bool { } } +func AllowVirtual(mm CppMethod) bool { + return true // AllowSignal(mm) +} + func AllowMethod(className string, mm CppMethod) error { for _, p := range mm.Parameters { diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index 7bbbfc32..d5726253 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -674,23 +674,27 @@ extern "C" { for _, c := range src.Classes { - cClassName := cabiClassName(c.ClassName) + methodPrefixName := cabiClassName(c.ClassName) for i, ctor := range c.Ctors { - ret.WriteString(fmt.Sprintf("%s %s_new%s(%s);\n", cClassName+"*", cClassName, maybeSuffix(i), emitParametersCabi(ctor, ""))) + ret.WriteString(fmt.Sprintf("%s %s_new%s(%s);\n", methodPrefixName+"*", methodPrefixName, maybeSuffix(i), emitParametersCabi(ctor, ""))) } for _, m := range c.Methods { - ret.WriteString(fmt.Sprintf("%s %s_%s(%s);\n", m.ReturnType.RenderTypeCabi(), cClassName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+cClassName+"*"))) + ret.WriteString(fmt.Sprintf("%s %s_%s(%s);\n", m.ReturnType.RenderTypeCabi(), methodPrefixName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*"))) if m.IsSignal { - ret.WriteString(fmt.Sprintf("%s %s_connect_%s(%s* self, intptr_t slot);\n", m.ReturnType.RenderTypeCabi(), cClassName, m.SafeMethodName(), cClassName)) + ret.WriteString(fmt.Sprintf("%s %s_connect_%s(%s* self, intptr_t slot);\n", m.ReturnType.RenderTypeCabi(), methodPrefixName, m.SafeMethodName(), methodPrefixName)) } } + for _, m := range c.VirtualMethods() { + ret.WriteString(fmt.Sprintf("void %s_override_virtual_%s(%s* self, intptr_t slot);\n", methodPrefixName, m.SafeMethodName(), methodPrefixName)) + } + // delete if c.CanDelete { - ret.WriteString(fmt.Sprintf("void %s_Delete(%s* self);\n", cClassName, cClassName)) + ret.WriteString(fmt.Sprintf("void %s_Delete(%s* self);\n", methodPrefixName, methodPrefixName)) } ret.WriteString("\n") @@ -736,7 +740,49 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { for _, c := range src.Classes { - cClassName := cabiClassName(c.ClassName) + methodPrefixName := cabiClassName(c.ClassName) + cppClassName := c.ClassName + virtualMethods := c.VirtualMethods() + + if len(virtualMethods) > 0 { + ret.WriteString("class MiqtVirtual" + cppClassName + " : public virtual " + cppClassName + " {\n" + + "public:\n" + + "\tusing " + cppClassName + "::" + cppClassName + ";\n" + // inherit constructors + "\n", + ) + for _, m := range virtualMethods { + + maybeReturn := ifv(m.ReturnType.RenderTypeQtCpp() == "void", "", "return ") + + ret.WriteString( + "\tintptr_t handle__" + m.SafeMethodName() + " = 0;\n" + + + "\n" + + + "\t" + m.ReturnType.RenderTypeQtCpp() + " " + m.MethodName + "(...) override {\n" + + "\t\tif (handle__" + m.SafeMethodName() + " == 0) {\n" + + "\t\t\t" + maybeReturn + methodPrefixName + "::" + m.MethodName + "(...);\n" + + "\t\t} else {\n" + + "\t\t\t" + maybeReturn + "miqt_exec_callback_" + methodPrefixName + "_" + m.SafeMethodName() + "(...);\n" + + "\t\t}\n" + + "\t}\n" + + + "\n" + + + "\t" + m.ReturnType.RenderTypeQtCpp() + " virtualbase_" + m.SafeMethodName() + "(...) {\n" + + "\t\t" + maybeReturn + methodPrefixName + "::" + m.MethodName + "(...);\n" + + "\t}\n" + + + "\n", + ) + } + + ret.WriteString( + "};\n" + + "\n") + + cppClassName = "MiqtVirtual" + cppClassName + } for i, ctor := range c.Ctors { @@ -754,9 +800,9 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "#endif\n"+ "}\n"+ "\n", - cClassName, cClassName, maybeSuffix(i), emitParametersCabi(ctor, ""), + methodPrefixName, methodPrefixName, maybeSuffix(i), emitParametersCabi(ctor, ""), preamble, - c.ClassName, forwarding, + cppClassName, forwarding, )) } else { @@ -766,9 +812,9 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "\treturn new %s(%s);\n"+ "}\n"+ "\n", - cClassName, cClassName, maybeSuffix(i), emitParametersCabi(ctor, ""), + methodPrefixName, methodPrefixName, maybeSuffix(i), emitParametersCabi(ctor, ""), preamble, - c.ClassName, forwarding, + cppClassName, forwarding, )) } @@ -800,7 +846,7 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "#endif\n"+ "}\n"+ "\n", - m.ReturnType.RenderTypeCabi(), cClassName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+cClassName+"*"), + m.ReturnType.RenderTypeCabi(), methodPrefixName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*"), preamble, emitAssignCppToCabi("\treturn ", m.ReturnType, callTarget), m.ReturnType.RenderTypeCabi(), @@ -808,10 +854,10 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { } else if m.BecomesNonConstInVersion != nil { - nonConstCallTarget := "const_cast<" + cClassName + "*>(self)->" + m.CppCallTarget() + "(" + forwarding + ")" + nonConstCallTarget := "const_cast<" + methodPrefixName + "*>(self)->" + m.CppCallTarget() + "(" + forwarding + ")" ret.WriteString("" + - m.ReturnType.RenderTypeCabi() + " " + cClassName + "_" + m.SafeMethodName() + "(" + emitParametersCabi(m, ifv(m.IsConst, "const ", "")+cClassName+"*") + ") {\n" + + m.ReturnType.RenderTypeCabi() + " " + methodPrefixName + "_" + m.SafeMethodName() + "(" + emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*") + ") {\n" + preamble + "\n" + "// This method was changed from const to non-const in Qt " + *m.BecomesNonConstInVersion + "\n" + "#if QT_VERSION < QT_VERSION_CHECK(" + strings.Replace(*m.BecomesNonConstInVersion, `.`, `,`, -1) + ",0)\n" + @@ -831,7 +877,7 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "%s"+ "}\n"+ "\n", - m.ReturnType.RenderTypeCabi(), cClassName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+cClassName+"*"), + m.ReturnType.RenderTypeCabi(), methodPrefixName, m.SafeMethodName(), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*"), preamble, emitAssignCppToCabi("\treturn ", m.ReturnType, callTarget), )) @@ -860,8 +906,8 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { signalCode += "\t\t" + bindingFunc + "(" + strings.Join(paramArgs, `, `) + ");\n" ret.WriteString( - `void ` + cClassName + `_connect_` + m.SafeMethodName() + `(` + cClassName + `* self, intptr_t slot) {` + "\n" + - "\t" + c.ClassName + `::connect(self, ` + exactSignal + `, self, [=](` + emitParametersCpp(m) + `) {` + "\n" + + `void ` + methodPrefixName + `_connect_` + m.SafeMethodName() + `(` + methodPrefixName + `* self, intptr_t slot) {` + "\n" + + "\t" + cppClassName + `::connect(self, ` + exactSignal + `, self, [=](` + emitParametersCpp(m) + `) {` + "\n" + signalCode + "\t});\n" + "}\n" + @@ -871,6 +917,17 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { } + // Virtual override helpers + for _, m := range virtualMethods { + ret.WriteString( + `void ` + methodPrefixName + `_override_virtual_` + m.SafeMethodName() + `(` + methodPrefixName + `* self, intptr_t slot) {` + "\n" + + "\tstatic_cast<" + cppClassName + ">(self)->handle__" + m.SafeMethodName() + " = slot;\n" + + "}\n" + + "\n", + ) + + } + // Delete if c.CanDelete { ret.WriteString(fmt.Sprintf( @@ -878,7 +935,7 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) { "\tdelete self;\n"+ "}\n"+ "\n", - cClassName, cClassName, + methodPrefixName, methodPrefixName, )) } } diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index b9d7b5c9..8dd6a600 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -238,6 +238,8 @@ type CppMethod struct { IsStatic bool IsSignal bool IsConst bool + IsVirtual bool + IsProtected bool // If true, we can't call this method but we may still be able to overload it HiddenParams []CppParameter // Populated if there is an overload with more parameters // Special quirks @@ -382,6 +384,56 @@ type CppClass struct { ChildEnums []CppEnum } +// Virtual checks if the class has any virtual methods. This requires global +// state knowledge as virtual methods might have been inherited. +// C++ constructors cannot be virtual. +func (c *CppClass) VirtualMethods() []CppMethod { + var ret []CppMethod + var retNames = make(map[string]struct{}, 0) // if name is present, a child class found it first + + for _, m := range c.Methods { + if !m.IsVirtual { + continue + } + if m.IsSignal { + continue + } + if !AllowVirtual(m) { + continue + } + + ret = append(ret, m) + retNames[m.MethodName] = struct{}{} + } + + for _, inh := range c.Inherits { + cinfo, ok := KnownClassnames[inh] + if !ok { + panic("Class " + c.ClassName + " inherits from unknown class " + inh) + } + + for _, m := range cinfo.Class.Methods { + if !m.IsVirtual { + continue + } + if m.IsSignal { + continue + } + if !AllowVirtual(m) { + continue + } + if _, ok := retNames[m.MethodName]; ok { + continue // Already found in a child class + } + + ret = append(ret, m) + retNames[m.MethodName] = struct{}{} + } + } + + return ret +} + type CppTypedef struct { Alias string UnderlyingType CppParameter