mirror of
https://github.com/mappu/miqt.git
synced 2024-12-22 08:58:37 +00:00
genbindings/slots: emit per-slot callback bindings
This commit is contained in:
parent
69767d7d04
commit
7f56b4147c
@ -7,6 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func (p CppParameter) RenderTypeCabi() string {
|
||||
|
||||
ret := p.ParameterType
|
||||
switch p.ParameterType {
|
||||
case "uchar":
|
||||
@ -334,6 +335,12 @@ func emitAssignCppToCabi(assignExpression string, p CppParameter, rvalue string)
|
||||
|
||||
} else if !t.QtClassType() || (t.QtClassType() && t.Pointer) { // QList<int>, QList<QFoo*>
|
||||
|
||||
// In some cases rvalue is a function call and the temporary
|
||||
// is necessary; in some cases it's a literal and the temporary is
|
||||
// elided; but in some cases it's a Qt class and the temporary goes
|
||||
// through a copy constructor
|
||||
// TODO Detect safe cases where this can be optimized
|
||||
|
||||
shouldReturn = p.RenderTypeQtCpp() + " " + namePrefix + "_ret = "
|
||||
|
||||
afterCall += indent + "// Convert QList<> from C++ memory to manually-managed C memory\n"
|
||||
@ -695,14 +702,32 @@ func emitBindingCpp(src *CppParsedHeader, filename string) (string, error) {
|
||||
}
|
||||
|
||||
if m.IsSignal {
|
||||
|
||||
bindingFunc := "miqt_exec_callback_" + cabiClassName(c.ClassName) + "_" + m.SafeMethodName()
|
||||
|
||||
// If there are hidden parameters, the type of the signal itself
|
||||
// needs to include them
|
||||
exactSignal := `static_cast<void (` + c.ClassName + `::*)(` + emitParameterTypesCpp(m, true) + `)>(&` + c.ClassName + `::` + m.CppCallTarget() + `)`
|
||||
|
||||
paramArgs := []string{"slot"}
|
||||
paramArgDefs := []string{"void* cb"}
|
||||
|
||||
var signalCode string
|
||||
|
||||
for i, p := range m.Parameters {
|
||||
signalCode += emitAssignCppToCabi(fmt.Sprintf("\t\t%s sigval%d = ", emitReturnTypeCabi(p), i+1), p, p.ParameterName)
|
||||
paramArgs = append(paramArgs, fmt.Sprintf("sigval%d", i+1))
|
||||
paramArgDefs = append(paramArgDefs, emitReturnTypeCabi(p)+" "+p.ParameterName)
|
||||
}
|
||||
|
||||
signalCode += "\t\t" + bindingFunc + "(" + strings.Join(paramArgs, `, `) + ");\n"
|
||||
|
||||
ret.WriteString(
|
||||
`void ` + cClassName + `_connect_` + m.SafeMethodName() + `(` + cClassName + `* self, void* slot) {` + "\n" +
|
||||
"void " + bindingFunc + "(" + strings.Join(paramArgDefs, `, `) + ");\n" +
|
||||
"\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" +
|
||||
signalCode +
|
||||
"\t});\n" +
|
||||
"}\n" +
|
||||
"\n",
|
||||
|
@ -106,8 +106,13 @@ func (p CppParameter) RenderTypeGo() string {
|
||||
|
||||
func (p CppParameter) parameterTypeCgo() string {
|
||||
if p.ParameterType == "QString" {
|
||||
return "C.char"
|
||||
return "*C.struct_miqt_string"
|
||||
}
|
||||
|
||||
if _, ok := p.QListOf(); ok {
|
||||
return "*C.struct_miqt_array"
|
||||
}
|
||||
|
||||
tmp := strings.Replace(p.RenderTypeCabi(), `*`, "", -1)
|
||||
if strings.HasPrefix(tmp, "const ") {
|
||||
tmp = tmp[6:] // Constness doesn't survive the CABI boundary
|
||||
@ -119,7 +124,13 @@ func (p CppParameter) parameterTypeCgo() string {
|
||||
tmp = "s" + tmp[7:] // Cgo uses schar
|
||||
}
|
||||
tmp = strings.Replace(tmp, `long long`, `longlong`, -1)
|
||||
return "C." + strings.Replace(tmp, " ", "_", -1)
|
||||
tmp = "C." + strings.Replace(tmp, " ", "_", -1)
|
||||
|
||||
if p.QtClassType() || p.Pointer || p.ByRef {
|
||||
return "*" + tmp
|
||||
} else {
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
func emitParametersGo(params []CppParameter) string {
|
||||
@ -194,12 +205,14 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
|
||||
// Go: convert T[] -> t* and len
|
||||
// CABI: create a real QList<>
|
||||
|
||||
gfs.imports["runtime"] = struct{}{}
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
|
||||
if listType.ParameterType == "QString" {
|
||||
// Combo
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
|
||||
preamble += "// For the C ABI, malloc two C arrays; raw char* pointers and their lengths\n"
|
||||
preamble += p.ParameterName + "_CArray := (*[0xffff]*" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
|
||||
preamble += p.ParameterName + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
|
||||
preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_CArray))\n"
|
||||
|
||||
preamble += "for i := range " + p.ParameterName + "{\n"
|
||||
@ -211,16 +224,12 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
|
||||
preamble += p.ParameterName + "_ma := &C.struct_miqt_array{len: C.size_t(len(" + p.ParameterName + ")), data: unsafe.Pointer(" + p.ParameterName + "_CArray)}\n"
|
||||
preamble += "defer runtime.KeepAlive(unsafe.Pointer(" + p.ParameterName + "_ma))\n"
|
||||
|
||||
tmp = append(tmp, p.ParameterName)
|
||||
tmp = append(tmp, p.ParameterName+"_ma")
|
||||
|
||||
} else {
|
||||
|
||||
preamble += "// For the C ABI, malloc a C array of raw pointers\n"
|
||||
if listType.QtClassType() {
|
||||
preamble += p.ParameterName + "_CArray := (*[0xffff]*" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
|
||||
} else {
|
||||
preamble += p.ParameterName + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
|
||||
}
|
||||
preamble += p.ParameterName + "_CArray := (*[0xffff]" + listType.parameterTypeCgo() + ")(C.malloc(C.size_t(8 * len(" + p.ParameterName + "))))\n"
|
||||
preamble += "defer C.free(unsafe.Pointer(" + p.ParameterName + "_CArray))\n"
|
||||
|
||||
preamble += "for i := range " + p.ParameterName + "{\n"
|
||||
@ -234,7 +243,7 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
|
||||
preamble += p.ParameterName + "_ma := &C.struct_miqt_array{len: C.size_t(len(" + p.ParameterName + ")), data: unsafe.Pointer(" + p.ParameterName + "_CArray)}\n"
|
||||
preamble += "defer runtime.KeepAlive(unsafe.Pointer(" + p.ParameterName + "_ma))\n"
|
||||
|
||||
tmp = append(tmp, p.ParameterName)
|
||||
tmp = append(tmp, p.ParameterName+"_ma")
|
||||
}
|
||||
|
||||
} else if p.Pointer && p.ParameterType == "char" {
|
||||
@ -252,7 +261,7 @@ func (gfs *goFileState) emitParametersGo2CABIForwarding(m CppMethod) (preamble s
|
||||
} else if p.IntType() || p.ParameterType == "bool" {
|
||||
if p.Pointer || p.ByRef {
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
tmp = append(tmp, "(*"+p.parameterTypeCgo()+")(unsafe.Pointer("+p.ParameterName+"))") // n.b. This may not work if the integer type conversion was wrong
|
||||
tmp = append(tmp, "("+p.parameterTypeCgo()+")(unsafe.Pointer("+p.ParameterName+"))") // n.b. This may not work if the integer type conversion was wrong
|
||||
} else {
|
||||
tmp = append(tmp, "("+p.parameterTypeCgo()+")("+p.ParameterName+")")
|
||||
}
|
||||
@ -526,13 +535,34 @@ import "C"
|
||||
if m.IsSignal {
|
||||
gfs.imports["unsafe"] = struct{}{}
|
||||
gfs.imports["runtime/cgo"] = struct{}{}
|
||||
ret.WriteString(`func (this *` + goClassName + `) On` + m.SafeMethodName() + `(slot func()) {
|
||||
var slotWrapper miqtCallbackFunc = func(argc C.int, args *C.void) {
|
||||
|
||||
var cgoNamedParams []string
|
||||
var paramNames []string
|
||||
for _, pp := range m.Parameters {
|
||||
cgoNamedParams = append(cgoNamedParams, pp.ParameterName+" "+pp.parameterTypeCgo())
|
||||
paramNames = append(paramNames, pp.ParameterName)
|
||||
}
|
||||
|
||||
ret.WriteString(`func (this *` + goClassName + `) On` + m.SafeMethodName() + `(slot func(` + emitParametersGo(m.Parameters) + `)) {
|
||||
slotWrapper := func(` + strings.Join(cgoNamedParams, `, `) + `) {
|
||||
// TODO convert CABI parameters to Go parameters here
|
||||
slot()
|
||||
}
|
||||
|
||||
C.` + goClassName + `_connect_` + m.SafeMethodName() + `(this.h, unsafe.Pointer(uintptr(cgo.NewHandle(slotWrapper))))
|
||||
}
|
||||
|
||||
//export miqt_exec_callback_` + goClassName + `_` + m.SafeMethodName() + `
|
||||
func miqt_exec_callback_` + goClassName + `_` + m.SafeMethodName() + `(cb *C.void` + ifv(len(m.Parameters) > 0, ", ", "") + strings.Join(cgoNamedParams, `, `) + `) {
|
||||
cfunc, ok := (cgo.Handle(uintptr(unsafe.Pointer(cb))).Value()).(func(` + strings.Join(cgoNamedParams, `, `) + `))
|
||||
if !ok {
|
||||
panic("miqt: callback of non-callback type (heap corruption?)")
|
||||
}
|
||||
|
||||
cfunc(` + strings.Join(paramNames, `, `) + ` )
|
||||
}
|
||||
|
||||
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
@ -2,23 +2,24 @@ package qt
|
||||
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
|
||||
#include "binding.h"
|
||||
|
||||
struct miqt_string* miqt_strdupg(_GoString_ gs) {
|
||||
return miqt_strdup(_GoStringPtr(gs), _GoStringLen(gs));
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"C"
|
||||
"runtime/cgo"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
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)
|
||||
// miqt_strdupg will strdup a Go string into a miqt_string*.
|
||||
// It is typed as returning an unsafe.Pointer because Cgo types cannot be shared
|
||||
// across Go file boundaries.
|
||||
func miqt_strdupg(s string) unsafe.Pointer {
|
||||
return unsafe.Pointer(C.miqt_strdupg(s))
|
||||
}
|
||||
|
@ -5,10 +5,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// miqt_exec_callback calls a Go function pointer for a connect() slot.
|
||||
// The function is defined in Go.
|
||||
void miqt_exec_callback(void* cb, int argc, void* argv);
|
||||
|
||||
struct miqt_string {
|
||||
size_t len;
|
||||
char data; // Data continues after this element, all in the same allocation
|
||||
|
@ -1,25 +0,0 @@
|
||||
package qt
|
||||
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
|
||||
#include "binding.h"
|
||||
|
||||
struct miqt_string* miqt_strdupg(_GoString_ gs) {
|
||||
return miqt_strdup(_GoStringPtr(gs), _GoStringLen(gs));
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// miqt_strdupg will strdup a Go string into a miqt_string*.
|
||||
// It is typed as returning an unsafe.Pointer because Cgo types cannot be shared
|
||||
// across Go file boundaries.
|
||||
func miqt_strdupg(s string) unsafe.Pointer {
|
||||
return unsafe.Pointer(C.miqt_strdupg(s))
|
||||
}
|
Loading…
Reference in New Issue
Block a user