2024-08-07 06:51:51 +00:00
package main
import (
"fmt"
2024-08-08 07:06:31 +00:00
"sort"
2024-08-07 06:51:51 +00:00
"strings"
)
2024-11-17 03:07:39 +00:00
// cppComment renders a string safely in a C++ block comment.
// It strips interior nested comments.
func cppComment ( s string ) string {
// Remove nested comments
uncomment := strings . NewReplacer ( "/*" , "" , "*/" , "" )
return "/* " + uncomment . Replace ( s ) + " */ "
}
2024-08-20 08:19:38 +00:00
func ( p CppParameter ) RenderTypeCabi ( ) string {
2024-09-14 07:31:39 +00:00
2024-09-16 07:33:07 +00:00
if p . ParameterType == "QString" {
2024-10-18 21:57:05 +00:00
return "struct miqt_string"
2024-09-16 07:33:07 +00:00
2024-10-18 23:24:49 +00:00
} else if p . ParameterType == "QByteArray" {
return "struct miqt_string"
2024-11-17 03:07:39 +00:00
} else if inner , ok := p . QListOf ( ) ; ok {
return "struct miqt_array " + cppComment ( "of " + inner . RenderTypeCabi ( ) )
2024-09-16 07:33:07 +00:00
2024-11-17 03:07:39 +00:00
} else if inner , ok := p . QSetOf ( ) ; ok {
return "struct miqt_array " + cppComment ( "set of " + inner . RenderTypeCabi ( ) )
2024-09-16 07:33:07 +00:00
2024-11-17 03:07:39 +00:00
} else if inner1 , inner2 , ok := p . QMapOf ( ) ; ok {
return "struct miqt_map " + cppComment ( "of " + inner1 . RenderTypeCabi ( ) + " to " + inner2 . RenderTypeCabi ( ) )
2024-11-04 07:59:03 +00:00
2024-11-17 03:07:39 +00:00
} else if inner1 , inner2 , ok := p . QPairOf ( ) ; ok {
return "struct miqt_map " + cppComment ( "tuple of " + inner1 . RenderTypeCabi ( ) + " and " + inner2 . RenderTypeCabi ( ) )
2024-11-15 05:41:36 +00:00
2024-09-16 07:33:07 +00:00
} else if ( p . Pointer || p . ByRef ) && p . QtClassType ( ) {
return cabiClassName ( p . ParameterType ) + "*"
} else if p . QtClassType ( ) && ! p . Pointer {
// Even if C++ returns by value, CABI is returning a heap copy (new'd, not malloc'd)
return cabiClassName ( p . ParameterType ) + "*"
}
2024-08-08 06:54:13 +00:00
ret := p . ParameterType
2024-08-14 06:34:05 +00:00
switch p . ParameterType {
2024-08-16 23:28:49 +00:00
case "uchar" :
ret = "unsigned char"
2024-08-14 06:34:05 +00:00
case "uint" :
ret = "unsigned int"
case "ulong" :
ret = "unsigned long"
2024-08-15 07:49:05 +00:00
case "qint8" :
ret = "int8_t"
case "quint8" :
ret = "uint8_t"
case "qint16" , "short" :
ret = "int16_t"
case "quint16" , "ushort" , "unsigned short" :
ret = "uint16_t"
case "qint32" :
ret = "int32_t"
case "quint32" :
ret = "uint32_t"
case "qlonglong" , "qint64" :
2024-08-14 06:34:05 +00:00
ret = "int64_t"
2024-08-15 07:49:05 +00:00
case "qulonglong" , "quint64" :
2024-08-14 06:34:05 +00:00
ret = "uint64_t"
2024-08-15 07:49:05 +00:00
case "qfloat16" :
ret = "_Float16" // No idea where this typedef comes from, but it exists
2024-08-18 06:56:09 +00:00
case "qreal" :
ret = "double"
2024-09-18 00:11:48 +00:00
case "qintptr" , "QIntegerForSizeof<void *>::Signed" :
2024-08-23 23:45:36 +00:00
ret = "intptr_t"
2024-09-18 00:11:48 +00:00
case "quintptr" , "uintptr" , "QIntegerForSizeof<void *>::Unsigned" :
2024-08-18 05:47:31 +00:00
ret = "uintptr_t"
2024-10-08 05:21:59 +00:00
case "qsizetype" , "qptrdiff" , "QIntegerForSizeof<std::size_t>::Signed" :
2024-08-26 10:51:21 +00:00
ret = "ptrdiff_t"
2024-08-14 06:34:05 +00:00
}
2024-08-29 05:38:18 +00:00
if p . Const {
// This is needed for const-correctness for calling some overloads
// e.g. QShortcut ctor taking (QWidget* parent, const char* member) signal -
// the signal/slot requires that member is const, not just plain char*
ret = "const " + ret
}
2024-09-19 07:39:17 +00:00
if ft , ok := p . QFlagsOf ( ) ; ok {
if e , ok := KnownEnums [ ft . ParameterType ] ; ok {
2024-10-16 05:07:05 +00:00
ret = e . Enum . UnderlyingType . RenderTypeCabi ( )
2024-09-19 07:39:17 +00:00
} else {
ret = "int"
}
2024-08-27 06:44:44 +00:00
2024-09-17 21:35:53 +00:00
} else if e , ok := KnownEnums [ p . ParameterType ] ; ok {
2024-10-16 05:07:05 +00:00
ret = e . Enum . UnderlyingType . RenderTypeCabi ( )
2024-09-17 21:35:53 +00:00
2024-08-25 05:50:14 +00:00
}
2024-08-28 06:22:05 +00:00
if p . Pointer {
ret += strings . Repeat ( "*" , p . PointerCount )
2024-08-29 07:01:12 +00:00
} else if p . ByRef {
ret += "*"
2024-08-28 06:22:05 +00:00
}
2024-08-14 06:34:05 +00:00
2024-09-18 01:08:18 +00:00
return ret
2024-08-08 06:54:13 +00:00
}
2024-09-18 01:08:18 +00:00
// RenderTypeQtCpp renders the Qt C++ type in the original form from the function
// definition, before any typedefs or transformations were applied.
2024-08-20 08:16:13 +00:00
func ( p CppParameter ) RenderTypeQtCpp ( ) string {
2024-09-18 01:08:18 +00:00
return p . GetQtCppType ( ) . RenderTypeIntermediateCpp ( )
}
// RenderTypeIntermediateCpp renders the Qt C++ type WITHOUT resolving the
// interior QtCppOriginalType. This is used for intermediate const_cast<>s.
func ( p CppParameter ) RenderTypeIntermediateCpp ( ) string {
cppType := p . ParameterType
2024-08-26 10:48:37 +00:00
2024-08-20 08:16:13 +00:00
if p . Const {
cppType = "const " + cppType
}
if p . Pointer {
2024-08-28 06:22:05 +00:00
cppType += strings . Repeat ( "*" , p . PointerCount )
2024-08-20 08:16:13 +00:00
}
if p . ByRef {
cppType += "&"
}
return cppType
}
2024-08-18 05:48:17 +00:00
// 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 {
2024-08-20 08:16:13 +00:00
tmp = append ( tmp , p . RenderTypeQtCpp ( ) + " " + p . ParameterName )
2024-08-18 05:48:17 +00:00
}
return strings . Join ( tmp , ` , ` )
}
2024-11-15 02:37:51 +00:00
func emitParameterNames ( m CppMethod ) string {
tmp := make ( [ ] string , 0 , len ( m . Parameters ) )
for _ , p := range m . Parameters {
tmp = append ( tmp , p . ParameterName )
}
return strings . Join ( tmp , ` , ` )
}
2024-09-07 03:25:25 +00:00
func emitParameterTypesCpp ( m CppMethod , includeHidden bool ) string {
2024-08-18 05:48:17 +00:00
tmp := make ( [ ] string , 0 , len ( m . Parameters ) )
for _ , p := range m . Parameters {
2024-08-20 08:16:13 +00:00
tmp = append ( tmp , p . RenderTypeQtCpp ( ) )
2024-08-18 05:48:17 +00:00
}
2024-09-07 03:25:25 +00:00
if includeHidden {
for _ , p := range m . HiddenParams {
tmp = append ( tmp , p . RenderTypeQtCpp ( ) )
}
}
2024-08-18 05:48:17 +00:00
return strings . Join ( tmp , ` , ` )
}
2024-08-09 06:41:29 +00:00
func emitParametersCabi ( m CppMethod , selfType string ) string {
tmp := make ( [ ] string , 0 , len ( m . Parameters ) + 1 )
2024-08-07 06:51:51 +00:00
2024-08-18 03:24:04 +00:00
if ! m . IsStatic && selfType != "" {
2024-08-08 06:55:47 +00:00
tmp = append ( tmp , selfType + " self" )
2024-08-07 06:51:51 +00:00
}
2024-08-09 06:41:29 +00:00
for _ , p := range m . Parameters {
2024-11-17 03:07:39 +00:00
tmp = append ( tmp , p . RenderTypeCabi ( ) + " " + p . ParameterName )
2024-08-07 06:51:51 +00:00
}
2024-08-09 06:41:29 +00:00
2024-08-07 06:51:51 +00:00
return strings . Join ( tmp , ", " )
}
2024-09-16 07:33:07 +00:00
func emitParametersCABI2CppForwarding ( params [ ] CppParameter , indent string ) ( preamble string , forwarding string ) {
2024-08-07 06:51:51 +00:00
tmp := make ( [ ] string , 0 , len ( params ) + 1 )
for _ , p := range params {
2024-09-16 07:33:07 +00:00
addPre , addFwd := emitCABI2CppForwarding ( p , indent )
preamble += addPre
tmp = append ( tmp , addFwd )
}
2024-08-08 06:54:13 +00:00
2024-09-16 07:33:07 +00:00
return preamble , strings . Join ( tmp , ", " )
}
2024-08-15 07:50:30 +00:00
2024-09-18 01:08:18 +00:00
func makeNamePrefix ( in string ) string {
2024-11-15 05:40:57 +00:00
replacer := strings . NewReplacer ( ` [ ` , ` _ ` , ` ] ` , "" , ` . ` , ` _ ` )
return replacer . Replace ( in )
2024-09-18 01:08:18 +00:00
}
2024-09-16 07:33:07 +00:00
func emitCABI2CppForwarding ( p CppParameter , indent string ) ( preamble string , forwarding string ) {
2024-08-15 07:50:30 +00:00
2024-09-18 01:08:18 +00:00
nameprefix := makeNamePrefix ( p . ParameterName )
2024-09-17 07:43:22 +00:00
2024-09-16 07:33:07 +00:00
if p . ParameterType == "QString" {
2024-10-18 21:57:05 +00:00
// The CABI received parameter is a struct miqt_string, passed by value
// C++ needs it as a QString. Create one on the stack for automatic cleanup
// The caller will free the miqt_string
preamble += indent + "QString " + nameprefix + "_QString = QString::fromUtf8(" + p . ParameterName + ".data, " + p . ParameterName + ".len);\n"
2024-09-17 07:43:22 +00:00
return preamble , nameprefix + "_QString"
2024-09-14 06:26:59 +00:00
2024-10-18 23:24:49 +00:00
} else if p . ParameterType == "QByteArray" {
// The caller will free the miqt_string data
// This ctor makes a deep copy, on the stack which will be dtor'd by RAII
preamble += indent + "QByteArray " + nameprefix + "_QByteArray(" + p . ParameterName + ".data, " + p . ParameterName + ".len);\n"
return preamble , nameprefix + "_QByteArray"
2024-09-16 07:33:07 +00:00
} else if listType , ok := p . QListOf ( ) ; ok {
2024-08-15 07:50:30 +00:00
2024-10-16 05:07:05 +00:00
preamble += indent + p . GetQtCppType ( ) . ParameterType + " " + nameprefix + "_QList;\n"
2024-11-04 07:18:11 +00:00
preamble += indent + nameprefix + "_QList.reserve(" + p . ParameterName + ".len);\n"
2024-08-11 04:37:18 +00:00
2024-11-04 07:18:11 +00:00
preamble += indent + listType . RenderTypeCabi ( ) + "* " + nameprefix + "_arr = static_cast<" + listType . RenderTypeCabi ( ) + "*>(" + p . ParameterName + ".data);\n"
preamble += indent + "for(size_t i = 0; i < " + p . ParameterName + ".len; ++i) {\n"
2024-08-26 10:51:21 +00:00
2024-09-17 07:43:22 +00:00
listType . ParameterName = nameprefix + "_arr[i]"
addPre , addFwd := emitCABI2CppForwarding ( listType , indent + "\t" )
preamble += addPre
preamble += indent + "\t" + nameprefix + "_QList.push_back(" + addFwd + ");\n"
2024-08-15 07:49:05 +00:00
2024-09-17 07:43:22 +00:00
preamble += indent + "}\n"
2024-11-04 09:51:52 +00:00
// Support passing QList<>* (very rare, but used in qnetwork)
if p . Pointer {
return preamble , "&" + nameprefix + "_QList"
} else {
return preamble , nameprefix + "_QList"
}
2024-08-14 06:33:47 +00:00
2024-11-04 07:59:03 +00:00
} else if kType , vType , ok := p . QMapOf ( ) ; ok {
preamble += indent + p . GetQtCppType ( ) . ParameterType + " " + nameprefix + "_QMap;\n"
// This container may be a QMap or a QHash
// QHash supports .reserve(), but QMap doesn't
if strings . HasPrefix ( p . ParameterType , "QHash<" ) {
preamble += indent + nameprefix + "_QMap.reserve(" + p . ParameterName + ".len);\n"
}
preamble += indent + kType . RenderTypeCabi ( ) + "* " + nameprefix + "_karr = static_cast<" + kType . RenderTypeCabi ( ) + "*>(" + p . ParameterName + ".keys);\n"
preamble += indent + vType . RenderTypeCabi ( ) + "* " + nameprefix + "_varr = static_cast<" + vType . RenderTypeCabi ( ) + "*>(" + p . ParameterName + ".values);\n"
preamble += indent + "for(size_t i = 0; i < " + p . ParameterName + ".len; ++i) {\n"
kType . ParameterName = nameprefix + "_karr[i]"
addPreK , addFwdK := emitCABI2CppForwarding ( kType , indent + "\t" )
preamble += addPreK
vType . ParameterName = nameprefix + "_varr[i]"
addPreV , addFwdV := emitCABI2CppForwarding ( vType , indent + "\t" )
preamble += addPreV
preamble += indent + "\t" + nameprefix + "_QMap[" + addFwdK + "] = " + addFwdV + ";\n"
preamble += indent + "}\n"
return preamble , nameprefix + "_QMap"
2024-11-15 05:41:36 +00:00
} else if kType , vType , ok := p . QPairOf ( ) ; ok {
preamble += indent + p . GetQtCppType ( ) . ParameterType + " " + nameprefix + "_QPair;\n"
preamble += indent + kType . RenderTypeCabi ( ) + "* " + nameprefix + "_first_arr = static_cast<" + kType . RenderTypeCabi ( ) + "*>(" + p . ParameterName + ".keys);\n"
preamble += indent + vType . RenderTypeCabi ( ) + "* " + nameprefix + "_second_arr = static_cast<" + vType . RenderTypeCabi ( ) + "*>(" + p . ParameterName + ".values);\n"
kType . ParameterName = nameprefix + "_first_arr[0]"
addPreK , addFwdK := emitCABI2CppForwarding ( kType , indent + "\t" )
preamble += addPreK
vType . ParameterName = nameprefix + "_second_arr[0]"
addPreV , addFwdV := emitCABI2CppForwarding ( vType , indent + "\t" )
preamble += addPreV
preamble += indent + nameprefix + "_QPair.first = " + addFwdK + ";\n"
preamble += indent + nameprefix + "_QPair.second = " + addFwdV + ";\n"
return preamble , nameprefix + "_QPair"
2024-09-19 07:39:17 +00:00
} else if p . IsFlagType ( ) || p . IntType ( ) || p . IsKnownEnum ( ) {
2024-09-16 07:33:07 +00:00
castSrc := p . ParameterName
castType := p . RenderTypeQtCpp ( )
2024-08-09 22:34:54 +00:00
2024-09-16 07:33:07 +00:00
if p . ByRef { // e.g. QDataStream::operator>>() overloads
castSrc = "*" + castSrc
}
2024-08-09 22:34:54 +00:00
2024-09-18 01:08:18 +00:00
if p . QtCppOriginalType != nil && p . QtCppOriginalType . Const != p . Const {
return preamble , "static_cast<" + p . RenderTypeQtCpp ( ) + ">(const_cast<" + p . RenderTypeIntermediateCpp ( ) + ">(" + p . ParameterName + "))"
}
2024-09-16 07:33:07 +00:00
if p . ParameterType == "qint64" ||
p . ParameterType == "quint64" ||
p . ParameterType == "qlonglong" ||
p . ParameterType == "qulonglong" ||
2024-10-11 04:30:26 +00:00
p . GetQtCppType ( ) . ParameterType == "qintptr" ||
p . GetQtCppType ( ) . ParameterType == "qsizetype" || // Qt 6 qversionnumber.h: invalid ‘ static_cast’ from type ‘ ptrdiff_t*’ {aka ‘ long int*’ } to type ‘ qsizetype*’ {aka ‘ long long int*’ }
2024-09-16 07:33:07 +00:00
p . ParameterType == "qint8" {
// QDataStream::operator>>() by reference (qint64)
// QLockFile::getLockInfo() by pointer
// QTextStream::operator>>() by reference (qlonglong + qulonglong)
// QDataStream::operator>>() qint8
// CABI has these as int64_t* (long int) which fails a static_cast to qint64& (long long int&)
// Hack a hard C-style cast
return preamble , "(" + castType + ")(" + castSrc + ")"
} else {
// Use static_cast<> safely
return preamble , "static_cast<" + castType + ">(" + castSrc + ")"
}
2024-08-09 23:46:48 +00:00
2024-09-17 09:48:26 +00:00
} else if _ , ok := p . QSetOf ( ) ; ok {
panic ( "QSet<> arguments are not yet implemented" ) // n.b. doesn't seem to exist in QtCore/QtGui/QtWidgets at all
2024-09-16 07:33:07 +00:00
} else if p . ByRef {
if p . Pointer {
// By ref and by pointer
// This happens for QDataStream &QDataStream::operator>>(char *&s)
// We are only using one level of indirection
return preamble , p . ParameterName
2024-08-08 06:54:13 +00:00
} else {
2024-09-16 07:33:07 +00:00
// By ref and not by pointer
// We changed RenderTypeCabi() to render this as a pointer
// Need to dereference so we can pass as reference to the actual Qt C++ function
//tmp = append(tmp, "*"+p.ParameterName)
return preamble , "*" + p . ParameterName
2024-08-08 06:54:13 +00:00
}
2024-09-16 07:33:07 +00:00
} else if p . QtClassType ( ) && ! p . Pointer {
// CABI takes all Qt types by pointer, even if C++ wants them by value
// Dereference the passed-in pointer
2024-09-17 07:43:22 +00:00
if strings . Contains ( p . ParameterName , ` [ ` ) {
return preamble , "*(" + p . ParameterName + ")" // Extra brackets aren't necessary, just nice
}
2024-09-16 07:33:07 +00:00
return preamble , "*" + p . ParameterName
} else {
return preamble , p . ParameterName
2024-08-07 06:51:51 +00:00
}
2024-08-08 06:54:13 +00:00
2024-08-07 06:51:51 +00:00
}
2024-09-11 06:04:32 +00:00
// emitAssignCppToCabi transforms and assigns rvalue to the assignExpression.
// Sample assignExpression: `return `, `auto foo = `
// Sample rvalue: `foo`, `foo(xyz)`
// The return is a complete statement including trailing newline.
func emitAssignCppToCabi ( assignExpression string , p CppParameter , rvalue string ) string {
2024-09-12 06:42:22 +00:00
shouldReturn := assignExpression // n.b. already has indent
2024-09-11 06:04:32 +00:00
afterCall := ""
2024-09-12 06:42:22 +00:00
assignExpression = strings . TrimLeft ( assignExpression , " \t" )
indent := shouldReturn [ 0 : len ( shouldReturn ) - len ( assignExpression ) ]
shouldReturn = shouldReturn [ len ( indent ) : ]
2024-09-11 06:04:32 +00:00
2024-09-18 01:08:18 +00:00
namePrefix := makeNamePrefix ( p . ParameterName )
2024-09-14 04:21:05 +00:00
2024-11-15 01:25:17 +00:00
if p . Void ( ) {
2024-09-11 06:04:32 +00:00
shouldReturn = ""
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
} else if p . ParameterType == "QString" {
if p . Pointer {
// e.g. QTextStream::String()
// These are rare, and probably expected to be lightweight references
// But, a copy is the best we can project it as
// Un-pointer-ify
2024-09-14 04:21:05 +00:00
shouldReturn = ifv ( p . Const , "const " , "" ) + "QString* " + namePrefix + "_ret = "
2024-09-12 06:42:22 +00:00
afterCall = indent + "// Convert QString pointer from UTF-16 in C++ RAII memory to UTF-8 in manually-managed C memory\n"
2024-09-14 04:21:05 +00:00
afterCall += indent + "QByteArray " + namePrefix + "_b = " + namePrefix + "_ret->toUtf8();\n"
2024-09-11 06:04:32 +00:00
} else {
2024-09-14 04:21:05 +00:00
shouldReturn = ifv ( p . Const , "const " , "" ) + "QString " + p . ParameterName + "_ret = "
2024-09-12 06:42:22 +00:00
afterCall = indent + "// Convert QString from UTF-16 in C++ RAII memory to UTF-8 in manually-managed C memory\n"
2024-09-14 04:21:05 +00:00
afterCall += indent + "QByteArray " + namePrefix + "_b = " + namePrefix + "_ret.toUtf8();\n"
2024-09-11 06:04:32 +00:00
}
2024-09-14 04:21:05 +00:00
2024-10-18 21:57:05 +00:00
afterCall += indent + "struct miqt_string " + namePrefix + "_ms;\n"
afterCall += indent + namePrefix + "_ms.len = " + namePrefix + "_b.length();\n"
afterCall += indent + namePrefix + "_ms.data = static_cast<char*>(malloc(" + namePrefix + "_ms.len));\n"
afterCall += indent + "memcpy(" + namePrefix + "_ms.data, " + namePrefix + "_b.data(), " + namePrefix + "_ms.len);\n"
afterCall += indent + assignExpression + namePrefix + "_ms;\n"
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
2024-10-18 23:24:49 +00:00
} else if p . ParameterType == "QByteArray" {
// C++ has given us a QByteArray. CABI needs this as a struct miqt_string
// Do not free the data, the caller will free it
shouldReturn = ifv ( p . Const , "const " , "" ) + "QByteArray " + p . ParameterName + "_qb = "
afterCall += indent + "struct miqt_string " + namePrefix + "_ms;\n"
afterCall += indent + namePrefix + "_ms.len = " + namePrefix + "_qb.length();\n"
afterCall += indent + namePrefix + "_ms.data = static_cast<char*>(malloc(" + namePrefix + "_ms.len));\n"
afterCall += indent + "memcpy(" + namePrefix + "_ms.data, " + namePrefix + "_qb.data(), " + namePrefix + "_ms.len);\n"
afterCall += indent + assignExpression + namePrefix + "_ms;\n"
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-10-18 23:24:49 +00:00
2024-09-11 06:04:32 +00:00
} else if t , ok := p . QListOf ( ) ; ok {
2024-09-17 06:28:14 +00:00
// 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
2024-09-11 06:04:32 +00:00
2024-09-17 06:28:14 +00:00
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
2024-09-14 06:26:59 +00:00
2024-09-17 06:28:14 +00:00
afterCall += indent + "// Convert QList<> from C++ memory to manually-managed C memory\n"
afterCall += indent + "" + t . RenderTypeCabi ( ) + "* " + namePrefix + "_arr = static_cast<" + t . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + t . RenderTypeCabi ( ) + ") * " + namePrefix + "_ret.length()));\n"
afterCall += indent + "for (size_t i = 0, e = " + namePrefix + "_ret.length(); i < e; ++i) {\n"
afterCall += emitAssignCppToCabi ( indent + "\t" + namePrefix + "_arr[i] = " , t , namePrefix + "_ret[i]" )
afterCall += indent + "}\n"
2024-09-14 06:26:59 +00:00
2024-11-04 07:18:11 +00:00
afterCall += indent + "struct miqt_array " + namePrefix + "_out;\n"
afterCall += indent + "" + namePrefix + "_out.len = " + namePrefix + "_ret.length();\n"
afterCall += indent + "" + namePrefix + "_out.data = static_cast<void*>(" + namePrefix + "_arr);\n"
2024-09-14 06:26:59 +00:00
2024-09-17 06:28:14 +00:00
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
2024-09-17 09:48:26 +00:00
} else if t , ok := p . QSetOf ( ) ; ok {
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
2024-10-16 05:07:05 +00:00
afterCall += indent + "// Convert QSet<> from C++ memory to manually-managed C memory\n"
2024-09-18 01:27:53 +00:00
afterCall += indent + "" + t . RenderTypeCabi ( ) + "* " + namePrefix + "_arr = static_cast<" + t . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + t . RenderTypeCabi ( ) + ") * " + namePrefix + "_ret.size()));\n"
2024-09-17 09:48:26 +00:00
afterCall += indent + "int " + namePrefix + "_ctr = 0;\n"
2024-09-18 01:27:53 +00:00
afterCall += indent + "QSetIterator<" + t . RenderTypeQtCpp ( ) + "> " + namePrefix + "_itr(" + namePrefix + "_ret);\n"
afterCall += indent + "while (" + namePrefix + "_itr.hasNext()) {\n"
afterCall += emitAssignCppToCabi ( indent + "\t" + namePrefix + "_arr[" + namePrefix + "_ctr++] = " , t , namePrefix + "_itr.next()" )
2024-09-17 09:48:26 +00:00
afterCall += indent + "}\n"
2024-11-04 07:18:11 +00:00
afterCall += indent + "struct miqt_array " + namePrefix + "_out;\n"
afterCall += indent + "" + namePrefix + "_out.len = " + namePrefix + "_ret.size();\n"
afterCall += indent + "" + namePrefix + "_out.data = static_cast<void*>(" + namePrefix + "_arr);\n"
2024-09-17 09:48:26 +00:00
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
2024-11-04 07:59:03 +00:00
} else if kType , vType , ok := p . QMapOf ( ) ; ok {
// QMap<K,V>
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
afterCall += indent + "// Convert QMap<> from C++ memory to manually-managed C memory\n"
afterCall += indent + "" + kType . RenderTypeCabi ( ) + "* " + namePrefix + "_karr = static_cast<" + kType . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + kType . RenderTypeCabi ( ) + ") * " + namePrefix + "_ret.size()));\n"
afterCall += indent + "" + vType . RenderTypeCabi ( ) + "* " + namePrefix + "_varr = static_cast<" + vType . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + vType . RenderTypeCabi ( ) + ") * " + namePrefix + "_ret.size()));\n"
afterCall += indent + "int " + namePrefix + "_ctr = 0;\n"
afterCall += indent + "for (auto " + namePrefix + "_itr = " + namePrefix + "_ret.keyValueBegin(); " + namePrefix + "_itr != " + namePrefix + "_ret.keyValueEnd(); ++" + namePrefix + "_itr) {\n"
afterCall += emitAssignCppToCabi ( indent + "\t" + namePrefix + "_karr[" + namePrefix + "_ctr] = " , kType , namePrefix + "_itr->first" )
afterCall += emitAssignCppToCabi ( indent + "\t" + namePrefix + "_varr[" + namePrefix + "_ctr] = " , vType , namePrefix + "_itr->second" )
afterCall += indent + "\t" + namePrefix + "_ctr++;\n"
afterCall += indent + "}\n"
afterCall += indent + "struct miqt_map " + namePrefix + "_out;\n"
afterCall += indent + "" + namePrefix + "_out.len = " + namePrefix + "_ret.size();\n"
afterCall += indent + "" + namePrefix + "_out.keys = static_cast<void*>(" + namePrefix + "_karr);\n"
afterCall += indent + "" + namePrefix + "_out.values = static_cast<void*>(" + namePrefix + "_varr);\n"
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-11-04 07:59:03 +00:00
2024-11-15 05:41:36 +00:00
} else if kType , vType , ok := p . QPairOf ( ) ; ok {
// QPair<T1,T2>
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
afterCall += indent + "// Convert QPair<> from C++ memory to manually-managed C memory\n"
afterCall += indent + "" + kType . RenderTypeCabi ( ) + "* " + namePrefix + "_first_arr = static_cast<" + kType . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + kType . RenderTypeCabi ( ) + ")));\n"
afterCall += indent + "" + vType . RenderTypeCabi ( ) + "* " + namePrefix + "_second_arr = static_cast<" + vType . RenderTypeCabi ( ) + "*>(malloc(sizeof(" + vType . RenderTypeCabi ( ) + ")));\n"
afterCall += emitAssignCppToCabi ( indent + namePrefix + "_first_arr[0] = " , kType , namePrefix + "_ret.first" )
afterCall += emitAssignCppToCabi ( indent + namePrefix + "_second_arr[0] = " , vType , namePrefix + "_ret.second" )
afterCall += indent + "struct miqt_map " + namePrefix + "_out;\n"
afterCall += indent + "" + namePrefix + "_out.len = 1;\n"
afterCall += indent + "" + namePrefix + "_out.keys = static_cast<void*>(" + namePrefix + "_first_arr);\n"
afterCall += indent + "" + namePrefix + "_out.values = static_cast<void*>(" + namePrefix + "_second_arr);\n"
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
} else if p . QtClassType ( ) && p . ByRef {
// It's a pointer in disguise, just needs one cast
2024-09-14 04:21:05 +00:00
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
2024-09-12 06:42:22 +00:00
afterCall += indent + "// Cast returned reference into pointer\n"
2024-09-11 06:04:32 +00:00
if p . Const {
nonConst := p // copy
nonConst . Const = false
nonConst . ByRef = false
nonConst . Pointer = true
nonConst . PointerCount = 1
2024-09-14 04:21:05 +00:00
afterCall += indent + "" + assignExpression + "const_cast<" + nonConst . RenderTypeQtCpp ( ) + ">(&" + namePrefix + "_ret);\n"
2024-09-11 06:04:32 +00:00
} else {
2024-09-14 04:21:05 +00:00
afterCall += indent + "" + assignExpression + "&" + namePrefix + "_ret;\n"
2024-09-11 06:04:32 +00:00
}
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
} else if p . QtClassType ( ) && ! p . Pointer {
2024-09-17 06:28:44 +00:00
// Elide temporary and emit directly from the rvalue
return indent + assignExpression + "new " + p . ParameterType + "(" + rvalue + ");\n"
2024-09-11 06:04:32 +00:00
2024-09-18 01:08:18 +00:00
} else if p . IsFlagType ( ) || p . IsKnownEnum ( ) || p . QtCppOriginalType != nil {
// Needs an explicit cast
2024-09-14 04:21:05 +00:00
shouldReturn = p . RenderTypeQtCpp ( ) + " " + namePrefix + "_ret = "
2024-09-11 06:04:32 +00:00
2024-09-18 01:08:18 +00:00
if p . QtCppOriginalType != nil && p . QtCppOriginalType . Const != p . Const {
afterCall += indent + "" + assignExpression + "const_cast<" + p . RenderTypeCabi ( ) + ">(static_cast<" + p . RenderTypeIntermediateCpp ( ) + ">(" + namePrefix + "_ret));\n"
} else {
afterCall += indent + "" + assignExpression + "static_cast<" + p . RenderTypeCabi ( ) + ">(" + namePrefix + "_ret);\n"
}
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
2024-09-18 00:11:48 +00:00
} else if p . Const {
shouldReturn += "(" + p . RenderTypeCabi ( ) + ") "
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-18 00:11:48 +00:00
2024-11-15 02:09:29 +00:00
} else {
// Basic type
if p . ByRef {
// The C++ type is a reference, the CABI type is a pointer type
shouldReturn += "&"
}
2024-11-15 02:10:52 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
}
}
2024-11-15 02:37:51 +00:00
func getCppZeroValue ( p CppParameter ) string {
if p . Pointer {
return "nullptr"
} else if p . IsKnownEnum ( ) {
return "(" + p . RenderTypeQtCpp ( ) + ")(0)"
} else if p . IntType ( ) {
return "0"
} else if p . ParameterType == "bool" {
return "false"
} else {
return p . RenderTypeQtCpp ( ) + "()"
}
}
2024-08-14 06:34:27 +00:00
// getReferencedTypes finds all referenced Qt types in this file.
2024-08-14 05:31:30 +00:00
func getReferencedTypes ( src * CppParsedHeader ) [ ] string {
2024-08-14 06:34:27 +00:00
2024-08-08 07:06:31 +00:00
foundTypes := map [ string ] struct { } { }
2024-11-04 07:48:56 +00:00
maybeAddType := func ( p CppParameter ) {
if p . QtClassType ( ) {
foundTypes [ p . ParameterType ] = struct { } { }
}
if t , ok := p . QListOf ( ) ; ok {
foundTypes [ "QList" ] = struct { } { } // FIXME or QVector?
if t . QtClassType ( ) {
foundTypes [ t . ParameterType ] = struct { } { }
}
}
2024-11-04 07:59:03 +00:00
if kType , vType , ok := p . QMapOf ( ) ; ok {
foundTypes [ "QMap" ] = struct { } { } // FIXME or QHash?
if kType . QtClassType ( ) {
foundTypes [ kType . ParameterType ] = struct { } { }
}
if vType . QtClassType ( ) {
foundTypes [ vType . ParameterType ] = struct { } { }
}
}
2024-11-04 07:48:56 +00:00
}
2024-08-08 07:06:31 +00:00
for _ , c := range src . Classes {
2024-08-14 06:34:27 +00:00
2024-08-08 07:06:31 +00:00
foundTypes [ c . ClassName ] = struct { } { }
2024-08-14 06:34:27 +00:00
2024-08-08 07:06:31 +00:00
for _ , ctor := range c . Ctors {
for _ , p := range ctor . Parameters {
2024-11-04 07:48:56 +00:00
maybeAddType ( p )
2024-08-08 07:06:31 +00:00
}
}
for _ , m := range c . Methods {
for _ , p := range m . Parameters {
2024-11-04 07:48:56 +00:00
maybeAddType ( p )
2024-08-14 06:34:27 +00:00
}
2024-11-04 07:48:56 +00:00
maybeAddType ( m . ReturnType )
2024-08-08 07:06:31 +00:00
}
2024-11-15 02:37:51 +00:00
for _ , vm := range c . VirtualMethods ( ) {
for _ , p := range vm . Parameters {
maybeAddType ( p )
}
maybeAddType ( vm . ReturnType )
}
2024-08-08 07:06:31 +00:00
}
2024-08-14 06:34:27 +00:00
2024-08-16 23:25:54 +00:00
// Some types (e.g. QRgb) are found but are typedefs, not classes
for _ , td := range src . Typedefs {
delete ( foundTypes , td . Alias )
}
// Convert to sorted list
2024-08-08 07:06:31 +00:00
foundTypesList := make ( [ ] string , 0 , len ( foundTypes ) )
for ft := range foundTypes {
2024-11-04 07:48:56 +00:00
if ! AllowClass ( ft ) {
2024-08-17 02:09:51 +00:00
continue
}
2024-08-14 05:31:30 +00:00
2024-08-08 07:06:31 +00:00
foundTypesList = append ( foundTypesList , ft )
}
sort . Strings ( foundTypesList )
2024-08-14 05:31:30 +00:00
return foundTypesList
}
2024-08-25 07:08:28 +00:00
// 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 {
2024-09-04 06:54:10 +00:00
// Many types are defined in qnamespace.h under Qt::
// The Go implementation is always called qt.Foo, and these names don't
// collide with anything, so strip the redundant prefix
className = strings . TrimPrefix ( className , ` Qt:: ` )
2024-08-25 07:08:28 +00:00
// Must use __ to avoid subclass/method name collision e.g. QPagedPaintDevice::Margins
return strings . Replace ( className , ` :: ` , ` __ ` , - 1 )
}
2024-11-04 07:59:03 +00:00
func cabiPreventStructDeclaration ( className string ) bool {
switch className {
case "QList" , "QString" , "QSet" , "QMap" , "QHash" :
return true // These types are reprojected
default :
return false
}
}
2024-10-16 05:07:05 +00:00
func emitBindingHeader ( src * CppParsedHeader , filename string , packageName string ) ( string , error ) {
2024-08-14 05:31:30 +00:00
ret := strings . Builder { }
2024-11-17 05:26:25 +00:00
includeGuard := "MIQT_" + strings . ToUpper ( strings . Replace ( strings . Replace ( packageName , ` / ` , ` _ ` , - 1 ) , ` - ` , ` _ ` , - 1 ) ) + "_GEN_" + strings . ToUpper ( strings . Replace ( strings . Replace ( filename , ` . ` , ` _ ` , - 1 ) , ` - ` , ` _ ` , - 1 ) )
2024-08-14 05:31:30 +00:00
2024-10-16 05:05:07 +00:00
bindingInclude := "../libmiqt/libmiqt.h"
2024-10-20 05:29:37 +00:00
if strings . Contains ( packageName , ` / ` ) {
2024-10-16 05:07:05 +00:00
bindingInclude = "../" + bindingInclude
}
2024-11-17 05:26:25 +00:00
ret . WriteString ( ` # pragma once
# ifndef ` + includeGuard + `
2024-08-14 05:31:30 +00:00
# define ` + includeGuard + `
# include < stdbool . h >
# include < stddef . h >
# include < stdint . h >
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2024-10-16 05:05:07 +00:00
# include "` + bindingInclude + `"
2024-09-12 06:47:31 +00:00
2024-08-14 05:31:30 +00:00
# ifdef __cplusplus
extern "C" {
# endif
` )
foundTypesList := getReferencedTypes ( src )
ret . WriteString ( "#ifdef __cplusplus\n" )
2024-08-08 07:06:31 +00:00
for _ , ft := range foundTypesList {
2024-11-04 07:59:03 +00:00
if cabiPreventStructDeclaration ( ft ) {
2024-08-14 06:34:27 +00:00
continue
}
2024-08-25 07:08:28 +00:00
if strings . Contains ( ft , ` :: ` ) {
// Forward declarations of inner classes are not yet supported in C++
// @ref https://stackoverflow.com/q/1021793
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" )
}
2024-08-14 05:31:30 +00:00
}
2024-08-11 04:37:18 +00:00
2024-08-14 05:31:30 +00:00
ret . WriteString ( "#else\n" )
for _ , ft := range foundTypesList {
2024-11-04 07:59:03 +00:00
if cabiPreventStructDeclaration ( ft ) {
2024-08-14 06:34:27 +00:00
continue
}
2024-08-25 07:08:28 +00:00
ret . WriteString ( ` typedef struct ` + cabiClassName ( ft ) + " " + cabiClassName ( ft ) + ";\n" )
2024-08-08 07:06:31 +00:00
}
2024-08-14 05:31:30 +00:00
ret . WriteString ( "#endif\n" )
2024-08-08 07:06:31 +00:00
ret . WriteString ( "\n" )
2024-08-07 06:56:14 +00:00
for _ , c := range src . Classes {
2024-08-07 06:51:51 +00:00
2024-11-11 05:27:28 +00:00
methodPrefixName := cabiClassName ( c . ClassName )
2024-08-25 07:08:28 +00:00
2024-08-07 06:56:14 +00:00
for i , ctor := range c . Ctors {
2024-11-11 05:27:28 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_new%s(%s);\n" , methodPrefixName + "*" , methodPrefixName , maybeSuffix ( i ) , emitParametersCabi ( ctor , "" ) ) )
2024-08-07 06:51:51 +00:00
}
2024-08-07 06:56:14 +00:00
for _ , m := range c . Methods {
2024-11-11 05:27:28 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_%s(%s);\n" , m . ReturnType . RenderTypeCabi ( ) , methodPrefixName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + methodPrefixName + "*" ) ) )
2024-08-18 05:48:17 +00:00
2024-09-07 03:25:25 +00:00
if m . IsSignal {
2024-11-11 05:27:28 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_connect_%s(%s* self, intptr_t slot);\n" , m . ReturnType . RenderTypeCabi ( ) , methodPrefixName , m . SafeMethodName ( ) , methodPrefixName ) )
2024-08-18 05:48:17 +00:00
}
2024-08-07 06:51:51 +00:00
}
2024-11-11 05:27:28 +00:00
for _ , m := range c . VirtualMethods ( ) {
2024-11-15 06:55:43 +00:00
ret . WriteString ( fmt . Sprintf ( "void %s_override_virtual_%s(%s* self, intptr_t slot);\n" , methodPrefixName , m . SafeMethodName ( ) , "void" /*methodPrefixName*/ ) )
2024-11-15 02:37:51 +00:00
2024-11-15 06:55:43 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_virtualbase_%s(%s);\n" , m . ReturnType . RenderTypeCabi ( ) , methodPrefixName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + "void" /*methodPrefixName*/ + "*" ) ) )
2024-11-11 05:27:28 +00:00
}
2024-08-09 22:32:57 +00:00
// delete
2024-08-20 08:10:57 +00:00
if c . CanDelete {
2024-11-11 05:27:28 +00:00
ret . WriteString ( fmt . Sprintf ( "void %s_Delete(%s* self);\n" , methodPrefixName , methodPrefixName ) )
2024-08-18 05:47:19 +00:00
}
2024-08-09 22:32:57 +00:00
2024-08-07 06:51:51 +00:00
ret . WriteString ( "\n" )
}
2024-08-09 06:41:29 +00:00
ret . WriteString (
` # ifdef __cplusplus
2024-08-07 06:51:51 +00:00
} /* extern C */
# endif
# endif
` )
return ret . String ( ) , nil
}
2024-11-15 02:37:51 +00:00
func fullyQualifiedConstructor ( className string ) string {
parts := strings . Split ( className , ` :: ` )
return className + "::" + parts [ len ( parts ) - 1 ]
}
2024-08-07 06:56:14 +00:00
func emitBindingCpp ( src * CppParsedHeader , filename string ) ( string , error ) {
2024-08-07 06:51:51 +00:00
ret := strings . Builder { }
2024-08-14 05:31:30 +00:00
for _ , ref := range getReferencedTypes ( src ) {
2024-08-18 04:08:25 +00:00
2024-08-29 05:17:12 +00:00
if ref == "QString" {
ret . WriteString ( "#include <QString>\n" )
ret . WriteString ( "#include <QByteArray>\n" )
ret . WriteString ( "#include <cstring>\n" )
continue
}
2024-08-25 07:08:28 +00:00
if strings . Contains ( ref , ` :: ` ) {
ret . WriteString ( ` #define WORKAROUND_INNER_CLASS_DEFINITION_ ` + cabiClassName ( ref ) + "\n" )
continue
}
2024-10-18 20:21:55 +00:00
if ! ImportHeaderForClass ( ref ) {
continue
}
2024-08-14 05:31:30 +00:00
ret . WriteString ( ` #include < ` + ref + ">\n" )
}
2024-10-16 05:07:05 +00:00
ret . WriteString ( ` #include < ` + filename + ">\n" )
2024-09-14 22:16:28 +00:00
ret . WriteString ( ` #include "gen_ ` + filename + "\"\n" )
ret . WriteString ( "#include \"_cgo_export.h\"\n\n" )
2024-08-14 05:31:30 +00:00
2024-08-07 06:56:14 +00:00
for _ , c := range src . Classes {
2024-08-07 06:51:51 +00:00
2024-11-11 05:27:28 +00:00
methodPrefixName := cabiClassName ( c . ClassName )
cppClassName := c . ClassName
virtualMethods := c . VirtualMethods ( )
if len ( virtualMethods ) > 0 {
2024-11-15 02:37:51 +00:00
overriddenClassName := "MiqtVirtual" + strings . Replace ( cppClassName , ` :: ` , ` ` , - 1 )
ret . WriteString ( "class " + overriddenClassName + " : public virtual " + cppClassName + " {\n" +
2024-11-11 05:27:28 +00:00
"public:\n" +
"\n" ,
)
2024-11-15 06:55:43 +00:00
for _ , ctor := range c . Ctors {
ret . WriteString ( "\t" + overriddenClassName + "(" + emitParametersCpp ( ctor ) + "): " + cppClassName + "(" + emitParameterNames ( ctor ) + ") {};\n" )
}
ret . WriteString ( "\n" )
2024-11-15 02:37:51 +00:00
if ! c . CanDelete {
2024-11-11 05:27:28 +00:00
ret . WriteString (
2024-11-15 02:37:51 +00:00
"private:\n" +
2024-11-15 06:55:43 +00:00
"\tvirtual ~" + overriddenClassName + "();\n" + // = delete;\n" +
2024-11-11 05:27:28 +00:00
"\n" +
2024-11-15 02:37:51 +00:00
"public:\n" +
"\n" ,
)
2024-11-15 06:55:43 +00:00
} else {
ret . WriteString (
"\tvirtual ~" + overriddenClassName + "() = default;\n" +
"\n" ,
)
2024-11-15 02:37:51 +00:00
}
2024-11-11 05:27:28 +00:00
2024-11-15 02:37:51 +00:00
for _ , m := range virtualMethods {
{
var maybeReturn , maybeReturn2 string
var returnTransformP , returnTransformF string
if ! m . ReturnType . Void ( ) {
maybeReturn = "return "
maybeReturn2 = m . ReturnType . RenderTypeCabi ( ) + " callback_return_value = "
returnParam := m . ReturnType // copy
returnParam . ParameterName = "callback_return_value"
returnTransformP , returnTransformF = emitCABI2CppForwarding ( returnParam , "\t\t" )
}
paramArgs := [ ] string { "handle__" + m . SafeMethodName ( ) }
var signalCode string
for i , p := range m . Parameters {
signalCode += emitAssignCppToCabi ( fmt . Sprintf ( "\t\t%s sigval%d = " , p . RenderTypeCabi ( ) , i + 1 ) , p , p . ParameterName )
paramArgs = append ( paramArgs , fmt . Sprintf ( "sigval%d" , i + 1 ) )
}
ret . WriteString (
"\t// cgo.Handle value for overwritten implementation\n" +
"\tintptr_t handle__" + m . SafeMethodName ( ) + " = 0;\n" +
"\n" ,
)
// In the case of method overloads, we always need to use the
// original method name (CppCallTarget), not the MethodName
ret . WriteString (
"\t// Subclass to allow providing a Go implementation\n" +
2024-11-15 06:55:43 +00:00
"\tvirtual " + m . ReturnType . RenderTypeQtCpp ( ) + " " + m . CppCallTarget ( ) + "(" + emitParametersCpp ( m ) + ") " + ifv ( m . IsConst , "const " , "" ) + "override {\n" +
2024-11-15 02:37:51 +00:00
"\t\tif (handle__" + m . SafeMethodName ( ) + " == 0) {\n" ,
)
if m . IsPureVirtual {
if m . ReturnType . Void ( ) {
ret . WriteString ( "\t\t\treturn; // Pure virtual, there is no base we can call\n" )
} else {
ret . WriteString ( "\t\t\treturn " + getCppZeroValue ( m . ReturnType ) + "; // Pure virtual, there is no base we can call\n" )
}
} else {
ret . WriteString ( "\t\t\t" + maybeReturn + methodPrefixName + "::" + m . CppCallTarget ( ) + "(" + emitParameterNames ( m ) + ");\n" )
2024-11-15 06:55:43 +00:00
if m . ReturnType . Void ( ) {
ret . WriteString ( "\t\t\treturn;\n" )
}
2024-11-15 02:37:51 +00:00
}
ret . WriteString (
2024-11-11 05:27:28 +00:00
"\t\t}\n" +
2024-11-15 02:37:51 +00:00
"\t\t\n" +
signalCode + "\n" +
"\t\t" + maybeReturn2 + "miqt_exec_callback_" + methodPrefixName + "_" + m . SafeMethodName ( ) + "(" + strings . Join ( paramArgs , ` , ` ) + ");\n" +
returnTransformP + "\n" +
"\t\t" + ifv ( maybeReturn == "" , "" , "return " + returnTransformF + ";" ) + "\n" +
"\t}\n" +
"\n" ,
)
}
2024-11-11 05:27:28 +00:00
2024-11-15 02:37:51 +00:00
// If there is a base version of this method, add a helper to
// allow calling it
2024-11-11 05:27:28 +00:00
2024-11-15 02:37:51 +00:00
if ! m . IsPureVirtual {
2024-11-11 05:27:28 +00:00
2024-11-15 02:37:51 +00:00
// The virtualbase wrapper needs to take CABI parameters, not
// real Qt parameters, in case there are protected enum types
// (e.g. QAbstractItemView::CursorAction)
var parametersCabi [ ] string
for _ , p := range m . Parameters {
parametersCabi = append ( parametersCabi , p . RenderTypeCabi ( ) + " " + p . ParameterName )
}
vbpreamble , vbforwarding := emitParametersCABI2CppForwarding ( m . Parameters , "\t\t" )
vbCallTarget := methodPrefixName + "::" + m . CppCallTarget ( ) + "(" + vbforwarding + ")"
ret . WriteString (
"\t// Wrapper to allow calling protected method\n" +
"\t" + m . ReturnType . RenderTypeCabi ( ) + " virtualbase_" + m . SafeMethodName ( ) + "(" + strings . Join ( parametersCabi , ", " ) + ") " + ifv ( m . IsConst , "const " , "" ) + "{\n" +
vbpreamble + "\n" +
emitAssignCppToCabi ( "\t\treturn " , m . ReturnType , vbCallTarget ) + "\n" +
"\t}\n" +
"\n" ,
)
}
2024-11-11 05:27:28 +00:00
}
ret . WriteString (
"};\n" +
"\n" )
2024-11-15 02:37:51 +00:00
cppClassName = overriddenClassName
2024-11-11 05:27:28 +00:00
}
2024-08-25 07:08:28 +00:00
2024-08-07 06:56:14 +00:00
for i , ctor := range c . Ctors {
2024-09-01 05:50:58 +00:00
2024-09-16 07:33:07 +00:00
preamble , forwarding := emitParametersCABI2CppForwarding ( ctor . Parameters , "\t" )
2024-09-01 05:50:58 +00:00
if ctor . LinuxOnly {
2024-09-01 06:50:19 +00:00
ret . WriteString ( fmt . Sprintf (
"%s* %s_new%s(%s) {\n" +
"#ifdef Q_OS_LINUX\n" +
"%s" +
"\treturn new %s(%s);\n" +
"#else\n" +
"\treturn nullptr;\n" +
"#endif\n" +
"}\n" +
"\n" ,
2024-11-11 05:27:28 +00:00
methodPrefixName , methodPrefixName , maybeSuffix ( i ) , emitParametersCabi ( ctor , "" ) ,
2024-09-01 06:50:19 +00:00
preamble ,
2024-11-11 05:27:28 +00:00
cppClassName , forwarding ,
2024-09-01 06:50:19 +00:00
) )
} else {
ret . WriteString ( fmt . Sprintf (
"%s* %s_new%s(%s) {\n" +
"%s" +
"\treturn new %s(%s);\n" +
"}\n" +
"\n" ,
2024-11-11 05:27:28 +00:00
methodPrefixName , methodPrefixName , maybeSuffix ( i ) , emitParametersCabi ( ctor , "" ) ,
2024-09-01 06:50:19 +00:00
preamble ,
2024-11-11 05:27:28 +00:00
cppClassName , forwarding ,
2024-09-01 06:50:19 +00:00
) )
2024-09-01 05:50:58 +00:00
}
2024-09-01 06:50:19 +00:00
2024-08-07 06:51:51 +00:00
}
2024-08-07 06:56:14 +00:00
for _ , m := range c . Methods {
2024-11-15 02:37:51 +00:00
// Protected virtual methods will be bound separately (the only
// useful thing is to expose calling the virtual base)
// Protected non-virtual methods should always be hidden
if m . IsProtected {
continue
}
2024-08-07 06:51:51 +00:00
// Need to take an extra 'self' parameter
2024-09-16 07:33:07 +00:00
preamble , forwarding := emitParametersCABI2CppForwarding ( m . Parameters , "\t" )
2024-08-08 06:54:13 +00:00
2024-09-11 06:04:32 +00:00
// callTarget is an rvalue representing the full C++ function call.
2024-08-18 03:24:04 +00:00
callTarget := "self->"
if m . IsStatic {
callTarget = c . ClassName + "::"
}
2024-09-11 06:04:32 +00:00
callTarget += m . CppCallTarget ( ) + "(" + forwarding + ")"
2024-09-01 05:50:58 +00:00
if m . LinuxOnly {
2024-09-01 06:50:19 +00:00
ret . WriteString ( fmt . Sprintf (
"%s %s_%s(%s) {\n" +
"#ifdef Q_OS_LINUX\n" +
"%s" +
2024-09-12 06:42:22 +00:00
"%s" +
2024-09-01 06:50:19 +00:00
"#else\n" +
"\t%s _ret_invalidOS;\n" +
"\treturn _ret_invalidOS;\n" +
"#endif\n" +
"}\n" +
"\n" ,
2024-11-11 05:27:28 +00:00
m . ReturnType . RenderTypeCabi ( ) , methodPrefixName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + methodPrefixName + "*" ) ,
2024-09-01 06:50:19 +00:00
preamble ,
2024-09-12 06:42:22 +00:00
emitAssignCppToCabi ( "\treturn " , m . ReturnType , callTarget ) ,
2024-09-16 07:33:07 +00:00
m . ReturnType . RenderTypeCabi ( ) ,
2024-09-01 06:50:19 +00:00
) )
2024-09-01 05:50:58 +00:00
2024-10-25 21:41:42 +00:00
} else if m . BecomesNonConstInVersion != nil {
2024-11-11 05:27:28 +00:00
nonConstCallTarget := "const_cast<" + methodPrefixName + "*>(self)->" + m . CppCallTarget ( ) + "(" + forwarding + ")"
2024-10-25 21:41:42 +00:00
ret . WriteString ( "" +
2024-11-11 05:27:28 +00:00
m . ReturnType . RenderTypeCabi ( ) + " " + methodPrefixName + "_" + m . SafeMethodName ( ) + "(" + emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + methodPrefixName + "*" ) + ") {\n" +
2024-10-25 21:41:42 +00:00
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" +
emitAssignCppToCabi ( "\treturn " , m . ReturnType , callTarget ) +
"#else\n" +
emitAssignCppToCabi ( "\treturn " , m . ReturnType , nonConstCallTarget ) +
"#endif\n" +
"}\n" +
"\n" ,
)
2024-09-01 06:50:19 +00:00
} else {
ret . WriteString ( fmt . Sprintf (
"%s %s_%s(%s) {\n" +
"%s" +
2024-09-12 06:42:22 +00:00
"%s" +
2024-09-01 06:50:19 +00:00
"}\n" +
"\n" ,
2024-11-11 05:27:28 +00:00
m . ReturnType . RenderTypeCabi ( ) , methodPrefixName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + methodPrefixName + "*" ) ,
2024-09-01 06:50:19 +00:00
preamble ,
2024-09-12 06:42:22 +00:00
emitAssignCppToCabi ( "\treturn " , m . ReturnType , callTarget ) ,
2024-09-01 06:50:19 +00:00
) )
}
2024-08-18 05:48:17 +00:00
2024-09-07 03:25:25 +00:00
if m . IsSignal {
2024-09-14 07:31:39 +00:00
bindingFunc := "miqt_exec_callback_" + cabiClassName ( c . ClassName ) + "_" + m . SafeMethodName ( )
2024-09-07 03:25:25 +00:00
// If there are hidden parameters, the type of the signal itself
// needs to include them
2024-10-11 04:29:49 +00:00
exactSignal := ` static_cast<void ( ` + c . ClassName + ` ::*)( ` + emitParameterTypesCpp ( m , true ) + ` ) ` + ifv ( m . IsConst , ` const ` , ` ` ) + ` >(& ` + c . ClassName + ` :: ` + m . CppCallTarget ( ) + ` ) `
2024-08-18 05:48:17 +00:00
2024-09-14 07:31:39 +00:00
paramArgs := [ ] string { "slot" }
2024-10-13 06:05:52 +00:00
paramArgDefs := [ ] string { "intptr_t cb" }
2024-09-14 07:31:39 +00:00
var signalCode string
for i , p := range m . Parameters {
2024-09-16 07:33:07 +00:00
signalCode += emitAssignCppToCabi ( fmt . Sprintf ( "\t\t%s sigval%d = " , p . RenderTypeCabi ( ) , i + 1 ) , p , p . ParameterName )
2024-09-14 07:31:39 +00:00
paramArgs = append ( paramArgs , fmt . Sprintf ( "sigval%d" , i + 1 ) )
2024-09-16 07:33:07 +00:00
paramArgDefs = append ( paramArgDefs , p . RenderTypeCabi ( ) + " " + p . ParameterName )
2024-09-14 07:31:39 +00:00
}
signalCode += "\t\t" + bindingFunc + "(" + strings . Join ( paramArgs , ` , ` ) + ");\n"
2024-08-18 05:48:17 +00:00
ret . WriteString (
2024-11-11 05:27:28 +00:00
` void ` + methodPrefixName + ` _connect_ ` + m . SafeMethodName ( ) + ` ( ` + methodPrefixName + ` * self, intptr_t slot) { ` + "\n" +
"\t" + cppClassName + ` ::connect(self, ` + exactSignal + ` , self, [=]( ` + emitParametersCpp ( m ) + ` ) { ` + "\n" +
2024-09-14 07:31:39 +00:00
signalCode +
2024-08-18 05:48:17 +00:00
"\t});\n" +
"}\n" +
"\n" ,
)
}
2024-09-01 05:50:58 +00:00
2024-08-07 06:51:51 +00:00
}
2024-08-09 22:32:57 +00:00
2024-11-11 05:27:28 +00:00
// Virtual override helpers
for _ , m := range virtualMethods {
2024-11-15 02:37:51 +00:00
// Virtual methods:
// 1. Allow overriding
// (Never use a const self*)
2024-11-11 05:27:28 +00:00
ret . WriteString (
2024-11-15 06:55:43 +00:00
` void ` + methodPrefixName + ` _override_virtual_ ` + m . SafeMethodName ( ) + ` (void* self, intptr_t slot) { ` + "\n" +
"\t( (" + cppClassName + "*)(self) )->handle__" + m . SafeMethodName ( ) + " = slot;\n" +
2024-11-11 05:27:28 +00:00
"}\n" +
"\n" ,
)
2024-11-15 02:37:51 +00:00
// 2. Add CABI function to call the base method
if ! m . IsPureVirtual {
// This is not generally exposed in the Go binding, but when overriding
// the method, allows Go code to call super()
// It uses CABI-CABI, the CABI-QtC++ type conversion will be done
// inside the class method so as to allow for accessing protected
// types.
// Both the parameters and return type are given in CABI format.
var parameterNames [ ] string
for _ , param := range m . Parameters {
parameterNames = append ( parameterNames , param . ParameterName )
}
// callTarget is an rvalue representing the full C++ function call.
// These are never static
2024-11-15 06:55:43 +00:00
callTarget := "( (" + ifv ( m . IsConst , "const " , "" ) + cppClassName + "*)(self) )->virtualbase_" + m . SafeMethodName ( ) + "(" + strings . Join ( parameterNames , ` , ` ) + ")"
2024-11-15 02:37:51 +00:00
ret . WriteString (
2024-11-15 06:55:43 +00:00
m . ReturnType . RenderTypeCabi ( ) + " " + methodPrefixName + "_virtualbase_" + m . SafeMethodName ( ) + "(" + emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + "void*" ) + ") {\n" +
2024-11-15 02:37:51 +00:00
"\t" + ifv ( m . ReturnType . Void ( ) , "" , "return " ) + callTarget + ";\n" +
"}\n" +
"\n" ,
)
}
2024-11-11 05:27:28 +00:00
}
2024-08-09 22:32:57 +00:00
// Delete
2024-08-20 08:10:57 +00:00
if c . CanDelete {
2024-08-18 05:47:19 +00:00
ret . WriteString ( fmt . Sprintf (
"void %s_Delete(%s* self) {\n" +
"\tdelete self;\n" +
"}\n" +
"\n" ,
2024-11-11 05:27:28 +00:00
methodPrefixName , methodPrefixName ,
2024-08-18 05:47:19 +00:00
) )
}
2024-08-07 06:51:51 +00:00
}
return ret . String ( ) , nil
}