mirror of
https://github.com/mappu/miqt.git
synced 2024-12-22 08:58:37 +00:00
genbindings: initial work on inner class definitions
This commit is contained in:
parent
b4df6d06f9
commit
f8a9a3f36e
@ -26,39 +26,16 @@ func parseHeader(topLevel []interface{}) (*CppParsedHeader, error) {
|
||||
switch kind {
|
||||
|
||||
case "CXXRecordDecl":
|
||||
// Must have a name
|
||||
nodename, ok := node["name"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("node has no name")
|
||||
}
|
||||
|
||||
log.Printf("-> %q name=%q\n", kind, nodename)
|
||||
|
||||
// Skip over forward class declarations
|
||||
// This is determined in two ways:
|
||||
// 1. If the class has no inner nodes
|
||||
nodeInner, ok := node["inner"].([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 2. If this class has only one `inner` entry that's a VisibilityAttr
|
||||
if len(nodeInner) == 1 {
|
||||
if node, ok := nodeInner[0].(map[string]interface{}); ok {
|
||||
if kind, ok := node["kind"].(string); ok && kind == "VisibilityAttr" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also skip over any custom exceptions
|
||||
if !AllowClass(nodename) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Process the inner class definition
|
||||
obj, err := processClassType(node, nodename)
|
||||
obj, err := processClassType(node, "")
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoContent) {
|
||||
log.Printf("-> Skipping (%v)\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// A real error (shouldn't happen)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -143,12 +120,42 @@ func parseHeader(topLevel []interface{}) (*CppParsedHeader, error) {
|
||||
return &ret, nil // done
|
||||
}
|
||||
|
||||
func processClassType(node map[string]interface{}, className string) (CppClass, error) {
|
||||
func processClassType(node map[string]interface{}, addNamePrefix string) (CppClass, error) {
|
||||
var ret CppClass
|
||||
ret.ClassName = className
|
||||
ret.CanDelete = true
|
||||
|
||||
inner, _ := node["inner"].([]interface{}) // Cannot fail, the parent call already checked that `inner` was present
|
||||
// Must have a name
|
||||
nodename, ok := node["name"].(string)
|
||||
if !ok {
|
||||
// This can happen for some nested class definitions e.g. qbytearraymatcher.h::Data
|
||||
return CppClass{}, ErrNoContent // errors.New("node has no name")
|
||||
}
|
||||
nodename = addNamePrefix + nodename
|
||||
ret.ClassName = nodename
|
||||
|
||||
log.Printf("-> Processing class %q...\n", nodename)
|
||||
|
||||
// Also skip over any custom exceptions
|
||||
if !AllowClass(nodename) {
|
||||
return CppClass{}, ErrNoContent
|
||||
}
|
||||
|
||||
// Skip over forward class declarations
|
||||
// This is determined in two ways:
|
||||
// 1. If the class has no inner nodes
|
||||
inner, ok := node["inner"].([]interface{})
|
||||
if !ok {
|
||||
return CppClass{}, ErrNoContent
|
||||
}
|
||||
|
||||
// 2. If this class has only one `inner` entry that's a VisibilityAttr
|
||||
if len(inner) == 1 {
|
||||
if node, ok := inner[0].(map[string]interface{}); ok {
|
||||
if kind, ok := node["kind"].(string); ok && kind == "VisibilityAttr" {
|
||||
return CppClass{}, ErrNoContent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this was 'struct' (default visible) or 'class' (default invisible)
|
||||
visibility := true
|
||||
@ -232,6 +239,24 @@ nextMethod:
|
||||
case "VisibilityAttr":
|
||||
// These seem to have no useful content
|
||||
|
||||
case "CXXRecordDecl":
|
||||
// Child class type definition e.g. QAbstractEventDispatcher::TimerInfo
|
||||
// Parse as a whole child class
|
||||
|
||||
if !visibility {
|
||||
continue // Skip private/protected
|
||||
}
|
||||
|
||||
child, err := processClassType(node, nodename+"::")
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoContent) {
|
||||
continue
|
||||
}
|
||||
panic(err) // A real problem
|
||||
}
|
||||
|
||||
ret.ChildClassdefs = append(ret.ChildClassdefs, child)
|
||||
|
||||
case "CXXConstructorDecl":
|
||||
|
||||
if isImplicit, ok := node["isImplicit"].(bool); ok && isImplicit {
|
||||
@ -409,7 +434,10 @@ func isExplicitlyDeleted(node map[string]interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var ErrTooComplex error = errors.New("Type declaration is too complex to parse")
|
||||
var (
|
||||
ErrTooComplex = errors.New("Type declaration is too complex to parse")
|
||||
ErrNoContent = errors.New("There's no content to include")
|
||||
)
|
||||
|
||||
func parseMethod(node map[string]interface{}, mm *CppMethod) error {
|
||||
|
||||
|
@ -350,6 +350,13 @@ func getReferencedTypes(src *CppParsedHeader) []string {
|
||||
return foundTypesList
|
||||
}
|
||||
|
||||
// cabiClassName returns the Go / CABI class name for a Qt C++ class.
|
||||
// Normally this is the same, except for class types that are nested inside another class definition.
|
||||
func cabiClassName(className string) string {
|
||||
// Must use __ to avoid subclass/method name collision e.g. QPagedPaintDevice::Margins
|
||||
return strings.Replace(className, `::`, `__`, -1)
|
||||
}
|
||||
|
||||
func emitBindingHeader(src *CppParsedHeader, filename string) (string, error) {
|
||||
ret := strings.Builder{}
|
||||
|
||||
@ -378,7 +385,26 @@ extern "C" {
|
||||
if ft == "QList" || ft == "QString" { // These types are reprojected
|
||||
continue
|
||||
}
|
||||
ret.WriteString(`class ` + ft + ";\n")
|
||||
|
||||
if strings.Contains(ft, `::`) {
|
||||
// Forward declarations of inner classes are not yet supported in C++
|
||||
// @ref https://stackoverflow.com/q/1021793
|
||||
|
||||
// ret.WriteString(`class ` + ft + ";\n")
|
||||
|
||||
//parent, child, _ := strings.Cut(ft, `::`)
|
||||
//ret.WriteString(`namespace ` + parent + ` { class ` + child + "; }\n")
|
||||
//ret.WriteString(`typedef ` + ft + " " + cabiClassName(ft) + ";\n")
|
||||
|
||||
ret.WriteString(`#if defined(WORKAROUND_INNER_CLASS_DEFINITION_` + cabiClassName(ft) + ")\n")
|
||||
ret.WriteString(`typedef ` + ft + " " + cabiClassName(ft) + ";\n")
|
||||
ret.WriteString("#else\n")
|
||||
ret.WriteString(`class ` + cabiClassName(ft) + ";\n")
|
||||
ret.WriteString("#endif\n")
|
||||
|
||||
} else {
|
||||
ret.WriteString(`class ` + ft + ";\n")
|
||||
}
|
||||
}
|
||||
|
||||
ret.WriteString("#else\n")
|
||||
@ -387,7 +413,7 @@ extern "C" {
|
||||
if ft == "QList" || ft == "QString" { // These types are reprojected
|
||||
continue
|
||||
}
|
||||
ret.WriteString(`typedef struct ` + ft + " " + ft + ";\n")
|
||||
ret.WriteString(`typedef struct ` + cabiClassName(ft) + " " + cabiClassName(ft) + ";\n")
|
||||
}
|
||||
|
||||
ret.WriteString("#endif\n")
|
||||
@ -396,21 +422,23 @@ extern "C" {
|
||||
|
||||
for _, c := range src.Classes {
|
||||
|
||||
cClassName := cabiClassName(c.ClassName)
|
||||
|
||||
for i, ctor := range c.Ctors {
|
||||
ret.WriteString(fmt.Sprintf("%s %s_new%s(%s);\n", c.ClassName+"*", c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, "")))
|
||||
ret.WriteString(fmt.Sprintf("%s %s_new%s(%s);\n", cClassName+"*", cClassName, maybeSuffix(i), emitParametersCabi(ctor, "")))
|
||||
}
|
||||
|
||||
for _, m := range c.Methods {
|
||||
ret.WriteString(fmt.Sprintf("%s %s_%s(%s);\n", emitReturnTypeCabi(m.ReturnType), c.ClassName, m.SafeMethodName(), emitParametersCabi(m, c.ClassName+"*")))
|
||||
ret.WriteString(fmt.Sprintf("%s %s_%s(%s);\n", emitReturnTypeCabi(m.ReturnType), cClassName, m.SafeMethodName(), emitParametersCabi(m, cClassName+"*")))
|
||||
|
||||
if m.IsSignal && !m.HasHiddenParams {
|
||||
ret.WriteString(fmt.Sprintf("%s %s_connect_%s(%s* self, void* slot);\n", emitReturnTypeCabi(m.ReturnType), c.ClassName, m.SafeMethodName(), c.ClassName))
|
||||
ret.WriteString(fmt.Sprintf("%s %s_connect_%s(%s* self, void* slot);\n", emitReturnTypeCabi(m.ReturnType), cClassName, m.SafeMethodName(), cClassName))
|
||||
}
|
||||
}
|
||||
|
||||
// delete
|
||||
if c.CanDelete {
|
||||
ret.WriteString(fmt.Sprintf("void %s_Delete(%s* self);\n", c.ClassName, c.ClassName))
|
||||
ret.WriteString(fmt.Sprintf("void %s_Delete(%s* self);\n", cClassName, cClassName))
|
||||
}
|
||||
|
||||
ret.WriteString("\n")
|
||||
@ -429,19 +457,24 @@ extern "C" {
|
||||
func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) {
|
||||
ret := strings.Builder{}
|
||||
|
||||
ret.WriteString(`#include "gen_` + filename + `"
|
||||
#include "` + filename + `"
|
||||
|
||||
`)
|
||||
|
||||
for _, ref := range getReferencedTypes(src) {
|
||||
if !ImportHeaderForClass(ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(ref, `::`) {
|
||||
ret.WriteString(`#define WORKAROUND_INNER_CLASS_DEFINITION_` + cabiClassName(ref) + "\n")
|
||||
continue
|
||||
}
|
||||
|
||||
ret.WriteString(`#include <` + ref + ">\n")
|
||||
}
|
||||
|
||||
ret.WriteString(`#include "gen_` + filename + `"
|
||||
#include "` + filename + `"
|
||||
|
||||
`)
|
||||
|
||||
ret.WriteString(`
|
||||
|
||||
extern "C" {
|
||||
@ -452,6 +485,8 @@ extern "C" {
|
||||
|
||||
for _, c := range src.Classes {
|
||||
|
||||
cClassName := cabiClassName(c.ClassName)
|
||||
|
||||
for i, ctor := range c.Ctors {
|
||||
preamble, forwarding := emitParametersCABI2CppForwarding(ctor.Parameters)
|
||||
ret.WriteString(fmt.Sprintf(
|
||||
@ -460,7 +495,7 @@ extern "C" {
|
||||
"\treturn new %s(%s);\n"+
|
||||
"}\n"+
|
||||
"\n",
|
||||
c.ClassName, c.ClassName, maybeSuffix(i), emitParametersCabi(ctor, ""),
|
||||
cClassName, cClassName, maybeSuffix(i), emitParametersCabi(ctor, ""),
|
||||
preamble,
|
||||
c.ClassName, forwarding,
|
||||
))
|
||||
@ -591,7 +626,7 @@ extern "C" {
|
||||
"%s"+
|
||||
"}\n"+
|
||||
"\n",
|
||||
emitReturnTypeCabi(m.ReturnType), c.ClassName, m.SafeMethodName(), emitParametersCabi(m, c.ClassName+"*"),
|
||||
emitReturnTypeCabi(m.ReturnType), cClassName, m.SafeMethodName(), emitParametersCabi(m, cClassName+"*"),
|
||||
preamble,
|
||||
shouldReturn, callTarget, nativeMethodName, forwarding,
|
||||
afterCall,
|
||||
@ -601,7 +636,7 @@ extern "C" {
|
||||
exactSignal := `static_cast<void (` + c.ClassName + `::*)(` + emitParameterTypesCpp(m) + `)>(&` + c.ClassName + `::` + nativeMethodName + `)`
|
||||
|
||||
ret.WriteString(
|
||||
`void ` + c.ClassName + `_connect_` + m.SafeMethodName() + `(` + c.ClassName + `* self, void* slot) {` + "\n" +
|
||||
`void ` + cClassName + `_connect_` + m.SafeMethodName() + `(` + cClassName + `* self, void* slot) {` + "\n" +
|
||||
"\t" + c.ClassName + `::connect(self, ` + exactSignal + `, self, [=](` + emitParametersCpp(m) + `) {` + "\n" +
|
||||
"\t\t" + `miqt_exec_callback(slot, 0, nullptr);` + "\n" +
|
||||
"\t});\n" +
|
||||
@ -618,7 +653,7 @@ extern "C" {
|
||||
"\tdelete self;\n"+
|
||||
"}\n"+
|
||||
"\n",
|
||||
c.ClassName, c.ClassName,
|
||||
cClassName, cClassName,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -287,9 +287,11 @@ import "C"
|
||||
|
||||
for _, c := range src.Classes {
|
||||
|
||||
goClassName := cabiClassName(c.ClassName)
|
||||
|
||||
ret.WriteString(`
|
||||
type ` + c.ClassName + ` struct {
|
||||
h *C.` + c.ClassName + `
|
||||
type ` + goClassName + ` struct {
|
||||
h *C.` + goClassName + `
|
||||
`)
|
||||
|
||||
// Embed all inherited types to directly allow calling inherited methods
|
||||
@ -300,7 +302,7 @@ import "C"
|
||||
ret.WriteString(`
|
||||
}
|
||||
|
||||
func (this *` + c.ClassName + `) cPointer() *C.` + c.ClassName + ` {
|
||||
func (this *` + goClassName + `) cPointer() *C.` + goClassName + ` {
|
||||
if this == nil {
|
||||
return nil
|
||||
}
|
||||
@ -316,8 +318,8 @@ import "C"
|
||||
}
|
||||
|
||||
ret.WriteString(`
|
||||
func new` + c.ClassName + `(h *C.` + c.ClassName + `) *` + c.ClassName + ` {
|
||||
return &` + c.ClassName + `{` + localInit + `}
|
||||
func new` + goClassName + `(h *C.` + goClassName + `) *` + goClassName + ` {
|
||||
return &` + goClassName + `{` + localInit + `}
|
||||
}
|
||||
|
||||
`)
|
||||
@ -328,8 +330,8 @@ import "C"
|
||||
// that never happens in Go's type system.
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
ret.WriteString(`
|
||||
func new` + c.ClassName + `_U(h unsafe.Pointer) *` + c.ClassName + ` {
|
||||
return new` + c.ClassName + `( (*C.` + c.ClassName + `)(h) )
|
||||
func new` + goClassName + `_U(h unsafe.Pointer) *` + goClassName + ` {
|
||||
return new` + goClassName + `( (*C.` + goClassName + `)(h) )
|
||||
}
|
||||
|
||||
`)
|
||||
@ -337,10 +339,10 @@ import "C"
|
||||
for i, ctor := range c.Ctors {
|
||||
preamble, forwarding := gfs.emitParametersGo2CABIForwarding(ctor)
|
||||
ret.WriteString(`
|
||||
// New` + c.ClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object.
|
||||
func New` + c.ClassName + maybeSuffix(i) + `(` + emitParametersGo(ctor.Parameters) + `) *` + c.ClassName + ` {
|
||||
` + preamble + ` ret := C.` + c.ClassName + `_new` + maybeSuffix(i) + `(` + forwarding + `)
|
||||
return new` + c.ClassName + `(ret)
|
||||
// New` + goClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object.
|
||||
func New` + goClassName + maybeSuffix(i) + `(` + emitParametersGo(ctor.Parameters) + `) *` + goClassName + ` {
|
||||
` + preamble + ` ret := C.` + goClassName + `_new` + maybeSuffix(i) + `(` + forwarding + `)
|
||||
return new` + goClassName + `(ret)
|
||||
}
|
||||
|
||||
`)
|
||||
@ -462,15 +464,15 @@ import "C"
|
||||
|
||||
}
|
||||
|
||||
receiverAndMethod := `(this *` + c.ClassName + `) ` + m.SafeMethodName()
|
||||
receiverAndMethod := `(this *` + goClassName + `) ` + m.SafeMethodName()
|
||||
if m.IsStatic {
|
||||
receiverAndMethod = c.ClassName + `_` + m.SafeMethodName()
|
||||
receiverAndMethod = goClassName + `_` + m.SafeMethodName()
|
||||
}
|
||||
|
||||
ret.WriteString(`
|
||||
func ` + receiverAndMethod + `(` + emitParametersGo(m.Parameters) + `) ` + returnTypeDecl + ` {
|
||||
` + preamble +
|
||||
shouldReturn + ` C.` + c.ClassName + `_` + m.SafeMethodName() + `(` + forwarding + `)
|
||||
shouldReturn + ` C.` + goClassName + `_` + m.SafeMethodName() + `(` + forwarding + `)
|
||||
` + afterword + `}
|
||||
|
||||
`)
|
||||
@ -479,12 +481,12 @@ import "C"
|
||||
if m.IsSignal && !m.HasHiddenParams {
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
gfs.imports["runtime/cgo"] = struct{}{}
|
||||
ret.WriteString(`func (this *` + c.ClassName + `) On` + m.SafeMethodName() + `(slot func()) {
|
||||
ret.WriteString(`func (this *` + goClassName + `) On` + m.SafeMethodName() + `(slot func()) {
|
||||
var slotWrapper miqtCallbackFunc = func(argc C.int, args *C.void) {
|
||||
slot()
|
||||
}
|
||||
|
||||
C.` + c.ClassName + `_connect_` + m.SafeMethodName() + `(this.h, unsafe.Pointer(uintptr(cgo.NewHandle(slotWrapper))))
|
||||
C.` + goClassName + `_connect_` + m.SafeMethodName() + `(this.h, unsafe.Pointer(uintptr(cgo.NewHandle(slotWrapper))))
|
||||
}
|
||||
`)
|
||||
}
|
||||
@ -492,8 +494,8 @@ import "C"
|
||||
|
||||
if c.CanDelete {
|
||||
ret.WriteString(`
|
||||
func (this *` + c.ClassName + `) Delete() {
|
||||
C.` + c.ClassName + `_Delete(this.h)
|
||||
func (this *` + goClassName + `) Delete() {
|
||||
C.` + goClassName + `_Delete(this.h)
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
@ -184,6 +184,8 @@ type CppClass struct {
|
||||
Methods []CppMethod
|
||||
Props []CppProperty
|
||||
CanDelete bool
|
||||
|
||||
ChildClassdefs []CppClass
|
||||
}
|
||||
|
||||
type CppTypedef struct {
|
||||
|
@ -124,6 +124,7 @@ func main() {
|
||||
}
|
||||
|
||||
// AST transforms on our IL
|
||||
astTransformChildClasses(parsed) // must be first
|
||||
astTransformOptional(parsed)
|
||||
astTransformOverloads(parsed)
|
||||
|
||||
|
31
cmd/genbindings/transformchildclasses.go
Normal file
31
cmd/genbindings/transformchildclasses.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
// takeChildren recursively takes the children of the class.
|
||||
func takeChildren(c *CppClass) []CppClass {
|
||||
if len(c.ChildClassdefs) == 0 {
|
||||
return []CppClass{}
|
||||
}
|
||||
|
||||
var ret []CppClass
|
||||
for _, child := range c.ChildClassdefs {
|
||||
ret = append(ret, takeChildren(&child)...)
|
||||
|
||||
child.ChildClassdefs = nil
|
||||
ret = append(ret, child)
|
||||
}
|
||||
c.ChildClassdefs = nil
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// astTransformChildClasses expands all classes with child class definitions
|
||||
// into new top-level entries within the header.
|
||||
func astTransformChildClasses(parsed *CppParsedHeader) {
|
||||
var taken []CppClass
|
||||
|
||||
for i, _ := range parsed.Classes {
|
||||
taken = append(taken, takeChildren(&parsed.Classes[i])...)
|
||||
}
|
||||
|
||||
parsed.Classes = append(parsed.Classes, taken...)
|
||||
}
|
Loading…
Reference in New Issue
Block a user