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-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-09-16 07:33:07 +00:00
} else if _ , ok := p . QListOf ( ) ; ok {
return "struct miqt_array*"
} else if _ , ok := p . QSetOf ( ) ; ok {
return "struct miqt_array*"
} 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-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-08-08 06:54:13 +00:00
if p . ParameterType == "QString" {
2024-10-18 21:57:05 +00:00
tmp = append ( tmp , "struct miqt_string " + p . ParameterName )
2024-08-08 06:54:13 +00:00
2024-10-18 23:24:49 +00:00
} else if p . ParameterType == "QByteArray" {
tmp = append ( tmp , "struct miqt_string " + p . ParameterName )
2024-08-11 04:37:18 +00:00
} else if t , ok := p . QListOf ( ) ; ok {
2024-09-14 06:26:59 +00:00
tmp = append ( tmp , "struct miqt_array* /* of " + t . RenderTypeCabi ( ) + " */ " + p . ParameterName )
2024-08-11 04:37:18 +00:00
2024-09-17 09:48:26 +00:00
} else if t , ok := p . QSetOf ( ) ; ok {
tmp = append ( tmp , "struct miqt_array* /* Set of " + t . RenderTypeCabi ( ) + " */ " + p . ParameterName )
2024-08-09 22:36:36 +00:00
} else if p . QtClassType ( ) {
2024-08-28 06:22:25 +00:00
if p . ByRef || p . Pointer {
// Pointer to Qt type
// Replace with taking our PQ typedef by value
tmp = append ( tmp , cabiClassName ( p . ParameterType ) + "* " + p . ParameterName )
} else {
// Qt type passed by value
// The CABI will unconditionally take these by pointer and dereference them
// when passing to C++
tmp = append ( tmp , cabiClassName ( p . ParameterType ) + "* " + p . ParameterName )
}
2024-08-09 22:36:36 +00:00
2024-08-08 06:54:13 +00:00
} else {
2024-08-20 08:19:38 +00:00
// RenderTypeCabi renders both pointer+reference as pointers
tmp = append ( tmp , p . RenderTypeCabi ( ) + " " + p . ParameterName )
2024-08-08 06:54:13 +00:00
}
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 {
return strings . Replace ( strings . Replace ( in , ` [ ` , ` _ ` , - 1 ) , ` ] ` , "" , - 1 )
}
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-09-17 07:43:22 +00:00
preamble += indent + nameprefix + "_QList.reserve(" + p . ParameterName + "->len);\n"
2024-08-11 04:37:18 +00:00
2024-09-17 07:43:22 +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"
return preamble , nameprefix + "_QList"
2024-08-14 06:33:47 +00:00
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-09-11 06:04:32 +00:00
if p . ParameterType == "void" && ! p . Pointer {
shouldReturn = ""
} 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-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-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-09-17 06:28:14 +00:00
afterCall += indent + "struct miqt_array* " + namePrefix + "_out = static_cast<struct miqt_array*>(malloc(sizeof(struct miqt_array)));\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-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"
afterCall += indent + "struct miqt_array* " + namePrefix + "_out = static_cast<struct miqt_array*>(malloc(sizeof(struct miqt_array)));\n"
2024-09-18 01:27:53 +00:00
afterCall += indent + "" + namePrefix + "_out->len = " + namePrefix + "_ret.size();\n"
2024-09-17 09:48:26 +00:00
afterCall += indent + "" + namePrefix + "_out->data = static_cast<void*>(" + namePrefix + "_arr);\n"
afterCall += indent + assignExpression + "" + namePrefix + "_out;\n"
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
}
} 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-09-11 06:04:32 +00:00
2024-09-18 00:11:48 +00:00
} else if p . Const {
shouldReturn += "(" + p . RenderTypeCabi ( ) + ") "
2024-09-11 06:04:32 +00:00
}
2024-09-12 06:42:22 +00:00
return indent + shouldReturn + rvalue + ";\n" + afterCall
2024-09-11 06:04:32 +00:00
}
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 { } { }
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 {
if p . QtClassType ( ) {
foundTypes [ p . ParameterType ] = struct { } { }
}
2024-08-17 02:10:33 +00:00
if t , ok := p . QListOf ( ) ; ok {
foundTypes [ "QList" ] = struct { } { } // FIXME or QVector?
if t . QtClassType ( ) {
foundTypes [ t . ParameterType ] = struct { } { }
}
2024-08-14 06:34:27 +00:00
}
2024-08-08 07:06:31 +00:00
}
}
for _ , m := range c . Methods {
for _ , p := range m . Parameters {
if p . QtClassType ( ) {
foundTypes [ p . ParameterType ] = struct { } { }
}
2024-08-17 02:10:33 +00:00
if t , ok := p . QListOf ( ) ; ok {
foundTypes [ "QList" ] = struct { } { } // FIXME or QVector?
if t . QtClassType ( ) {
foundTypes [ t . ParameterType ] = struct { } { }
}
2024-08-14 06:34:27 +00:00
}
2024-08-08 07:06:31 +00:00
}
if m . ReturnType . QtClassType ( ) {
foundTypes [ m . ReturnType . ParameterType ] = struct { } { }
}
2024-08-17 02:10:33 +00:00
if t , ok := m . ReturnType . QListOf ( ) ; ok {
foundTypes [ "QList" ] = struct { } { } // FIXME or QVector?
if t . QtClassType ( ) {
foundTypes [ t . ParameterType ] = struct { } { }
}
2024-08-14 06:34:27 +00:00
}
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-10-16 05:07:05 +00:00
if strings . HasPrefix ( ft , "QList<" ) || strings . HasPrefix ( ft , "QVector<" ) { // TODO properly exclude via the QListOf() check above
2024-08-14 05:31:30 +00:00
continue
}
2024-08-17 02:09:51 +00:00
if strings . HasSuffix ( ft , "Private" ) { // qbrush.h finds QGradientPrivate
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-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-10-20 06:04:18 +00:00
includeGuard := "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-08-14 05:31:30 +00:00
ret . WriteString ( ` #ifndef ` + includeGuard + `
# 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-08-17 00:39:45 +00:00
if ft == "QList" || ft == "QString" { // These types are reprojected
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-08-17 00:39:45 +00:00
if ft == "QList" || ft == "QString" { // These types are reprojected
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-08-25 07:08:28 +00:00
cClassName := cabiClassName ( c . ClassName )
2024-08-07 06:56:14 +00:00
for i , ctor := range c . Ctors {
2024-08-25 07:08:28 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_new%s(%s);\n" , cClassName + "*" , cClassName , 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-09-16 07:33:07 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_%s(%s);\n" , m . ReturnType . RenderTypeCabi ( ) , cClassName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + cClassName + "*" ) ) )
2024-08-18 05:48:17 +00:00
2024-09-07 03:25:25 +00:00
if m . IsSignal {
2024-10-13 06:05:52 +00:00
ret . WriteString ( fmt . Sprintf ( "%s %s_connect_%s(%s* self, intptr_t slot);\n" , m . ReturnType . RenderTypeCabi ( ) , cClassName , m . SafeMethodName ( ) , cClassName ) )
2024-08-18 05:48:17 +00:00
}
2024-08-07 06:51:51 +00:00
}
2024-08-09 22:32:57 +00:00
// delete
2024-08-20 08:10:57 +00:00
if c . CanDelete {
2024-08-25 07:08:28 +00:00
ret . WriteString ( fmt . Sprintf ( "void %s_Delete(%s* self);\n" , cClassName , cClassName ) )
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-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-08-25 07:08:28 +00:00
cClassName := cabiClassName ( c . ClassName )
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" ,
cClassName , cClassName , maybeSuffix ( i ) , emitParametersCabi ( ctor , "" ) ,
preamble ,
c . ClassName , forwarding ,
) )
} else {
ret . WriteString ( fmt . Sprintf (
"%s* %s_new%s(%s) {\n" +
"%s" +
"\treturn new %s(%s);\n" +
"}\n" +
"\n" ,
cClassName , cClassName , maybeSuffix ( i ) , emitParametersCabi ( ctor , "" ) ,
preamble ,
c . ClassName , forwarding ,
) )
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-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-09-16 07:33:07 +00:00
m . ReturnType . RenderTypeCabi ( ) , cClassName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + cClassName + "*" ) ,
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 {
nonConstCallTarget := "const_cast<" + cClassName + "*>(self)->" + m . CppCallTarget ( ) + "(" + forwarding + ")"
ret . WriteString ( "" +
m . ReturnType . RenderTypeCabi ( ) + " " + cClassName + "_" + m . SafeMethodName ( ) + "(" + emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + cClassName + "*" ) + ") {\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" +
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-09-16 07:33:07 +00:00
m . ReturnType . RenderTypeCabi ( ) , cClassName , m . SafeMethodName ( ) , emitParametersCabi ( m , ifv ( m . IsConst , "const " , "" ) + cClassName + "*" ) ,
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-10-13 06:05:52 +00:00
` void ` + cClassName + ` _connect_ ` + m . SafeMethodName ( ) + ` ( ` + cClassName + ` * self, intptr_t slot) { ` + "\n" +
2024-08-18 05:48:17 +00:00
"\t" + c . ClassName + ` ::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
// 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-08-25 07:08:28 +00:00
cClassName , cClassName ,
2024-08-18 05:47:19 +00:00
) )
}
2024-08-07 06:51:51 +00:00
}
return ret . String ( ) , nil
}