genbindings/connect: initial implementation

This commit is contained in:
mappu 2024-08-18 17:48:17 +12:00
parent c8ac62fccd
commit f488e54f8b
3 changed files with 100 additions and 2 deletions

View File

@ -68,6 +68,49 @@ func emitReturnTypeCabi(p CppParameter) string {
} }
} }
// emitParametersCpp emits the parameter definitions exactly how Qt C++ defines them.
func emitParametersCpp(m CppMethod) string {
tmp := make([]string, 0, len(m.Parameters))
for _, p := range m.Parameters {
cppType := p.ParameterType
if p.Const {
cppType = "const " + cppType
}
if p.Pointer {
cppType += "*"
}
if p.ByRef {
cppType += "&"
}
tmp = append(tmp, cppType+" "+p.ParameterName)
}
return strings.Join(tmp, `, `)
}
func emitParameterTypesCpp(m CppMethod) string {
tmp := make([]string, 0, len(m.Parameters))
for _, p := range m.Parameters {
cppType := p.ParameterType
if p.Const {
cppType = "const " + cppType
}
if p.Pointer {
cppType += "*"
}
if p.ByRef {
cppType += "&"
}
tmp = append(tmp, cppType)
}
return strings.Join(tmp, `, `)
}
func emitParametersCabi(m CppMethod, selfType string) string { func emitParametersCabi(m CppMethod, selfType string) string {
tmp := make([]string, 0, len(m.Parameters)+1) tmp := make([]string, 0, len(m.Parameters)+1)
@ -342,6 +385,10 @@ extern "C" {
for _, m := range c.Methods { 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), c.ClassName, m.SafeMethodName(), emitParametersCabi(m, c.ClassName+"*")))
if m.IsSignal {
ret.WriteString(fmt.Sprintf("%s %s_connect_%s(void* slot);\n", emitReturnTypeCabi(m.ReturnType), c.ClassName, m.SafeMethodName()))
}
} }
// delete // delete
@ -383,7 +430,13 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) {
ret.WriteString(`#include <` + ref + ">\n") ret.WriteString(`#include <` + ref + ">\n")
} }
ret.WriteString("\n") ret.WriteString(`
extern "C" {
extern void miqt_exec_callback(void* cb, int argc, void* argv);
}
`)
for _, c := range src.Classes { for _, c := range src.Classes {
@ -496,6 +549,19 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) {
shouldReturn, callTarget, nativeMethodName, forwarding, shouldReturn, callTarget, nativeMethodName, forwarding,
afterCall, afterCall,
)) ))
if m.IsSignal {
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" +
"\t" + c.ClassName + `::connect(self, ` + exactSignal + `, self, [=](` + emitParametersCpp(m) + `) {` + "\n" +
"\t\t" + `miqt_exec_callback(slot, 0, nullptr);` + "\n" +
"\t});\n" +
"}\n" +
"\n",
)
}
} }
// Delete // Delete

View File

@ -230,7 +230,7 @@ func emitParametersGo2CABIForwarding(m CppMethod) (preamble string, fowarding st
func emitGo(src *CppParsedHeader, headerName string) (string, error) { func emitGo(src *CppParsedHeader, headerName string) (string, error) {
ret := strings.Builder{} ret := strings.Builder{}
ret.WriteString(`package miqt ret.WriteString(`package qt
/* /*
@ -374,6 +374,20 @@ import "C"
` + afterword + `} ` + afterword + `}
`) `)
// Add Connect() wrappers for signal functions
if m.IsSignal {
imports["unsafe"] = struct{}{}
imports["runtime/cgo"] = struct{}{}
ret.WriteString(`func (this *` + c.ClassName + `) 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))))
}
`)
}
} }
if AllowDelete(c) { if AllowDelete(c) {

18
qt/binding.go Normal file
View File

@ -0,0 +1,18 @@
package qt
// SPDX-License-Identifier: MIT
type miqtCallbackFunc func(argc C.int, args *C.void)
//export miqt_exec_callback
func miqt_exec_callback(cb *C.void, argc C.int, args *C.void) {
// Our CABI for all callbacks is void(int, void*).
// Our Go ABI is CallbackFunc
// Then the Go bindings can unmarshal the arguments and C.free() them as necessary
cfunc, ok := (cgo.Handle(uintptr(unsafe.Pointer(cb))).Value()).(miqtCallbackFunc)
if !ok {
panic("miqt: callback of non-callback type (heap corruption?)")
}
cfunc(argc, args)
}