Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 90 additions & 22 deletions cmd/genbindings/clang2il.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ func parseMethod(node *AstNode, mm *CppMethod) error {
// If anything here is too complicated, skip the whole method

var err error = nil
mm.ReturnType, mm.Parameters, mm.IsConst, mm.IsNoExcept, err = parseTypeString(qualType)
mm.ReturnType, mm.Parameters, mm.IsConst, mm.Noexcept, err = parseTypeString(qualType)
if err != nil {
return err
}
Expand Down Expand Up @@ -786,46 +786,114 @@ func parseMethod(node *AstNode, mm *CppMethod) error {
// into its (A) return type and (B) separate parameter types.
// These clang strings never contain the parameter's name, so the names here are
// not filled in.
func parseTypeString(typeString string) (CppParameter, []CppParameter, bool, bool, error) {

if strings.Contains(typeString, `&&`) { // TODO Rvalue references
return CppParameter{}, nil, false, false, ErrTooComplex
func parseTypeString(typeString string) (CppParameter, []CppParameter, bool, string, error) {
// Remove leading/trailing spaces
typeString = strings.TrimSpace(typeString)

// Quick check for rvalue references, which we don't support
if strings.Contains(typeString, "&&") {
return CppParameter{}, nil, false, "", ErrTooComplex
}

// Find the outermost parentheses for the parameter list
opos := -1
epos := -1
depth := 0
parseParenLoop:
for i, c := range typeString {
switch c {
case '(':
if depth == 0 && opos == -1 {
opos = i
}
depth++
case ')':
depth--
if depth == 0 && opos != -1 {
epos = i
break parseParenLoop
}
}
if epos != -1 {
break
}
}

// Cut to exterior-most (, ) pair
opos := strings.Index(typeString, `(`)
epos := strings.LastIndex(typeString, `)`)

if opos == -1 || epos == -1 {
return CppParameter{}, nil, false, false, fmt.Errorf("Type string %q missing brackets", typeString)
return CppParameter{}, nil, false, "", fmt.Errorf("Type string %q missing brackets", typeString)
}

// Check for trailing const and noexcept (may be after parentheses)
after := typeString[epos+1:]
isConst := false
noexceptExpr := ""
after = strings.TrimSpace(after)
// Handle cases like "const noexcept", "noexcept const", etc.
for {
if strings.HasPrefix(after, "const") {
isConst = true
after = strings.TrimSpace(after[len("const"):])
} else if strings.HasPrefix(after, "noexcept") {
after = after[len("noexcept"):]
after = strings.TrimSpace(after)
// Handle noexcept with parenthesis: noexcept(expr)
if strings.HasPrefix(after, "(") {
// Find the full parenthesis expression
parens := 1
exprEnd := -1
for i := 1; i < len(after); i++ {
if after[i] == '(' {
parens++
} else if after[i] == ')' {
parens--
if parens == 0 {
exprEnd = i
break
}
}
}
if exprEnd != -1 {
noexceptExpr = "noexcept" + strings.TrimSpace(after[:exprEnd+1])
after = after[exprEnd+1:]
} else {
return CppParameter{}, nil, false, "", fmt.Errorf("noexcept string %q missing brackets", after)
}
} else {
noexceptExpr = "noexcept"
}
after = strings.TrimSpace(after)
} else {
break
}
}

isConst := strings.Contains(typeString[epos:], `const`)
isNoExcept := strings.Contains(typeString[epos:], `noexcept`)

returnType := parseSingleTypeString(strings.TrimSpace(typeString[0:opos]))
// Return type is everything before the first '('
returnType := parseSingleTypeString(strings.TrimSpace(typeString[:opos]))

// Parameter list is between the outermost parentheses
inner := typeString[opos+1 : epos]

// Should be no more brackets
if strings.ContainsAny(inner, `()`) {
return CppParameter{}, nil, false, false, ErrTooComplex
// If the parameter list is empty, return no parameters
if strings.TrimSpace(inner) == "" {
return returnType, []CppParameter{}, isConst, noexceptExpr, nil
}

// Parameters are separated by commas and nesting can not be possible
params := tokenizeMultipleParameters(inner) // strings.Split(inner, `,`)
// Tokenize parameters, handling function pointers and templates
params := tokenizeMultipleParameters(inner)

ret := make([]CppParameter, 0, len(params))
for _, p := range params {

p = strings.TrimSpace(p)
if p == "" {
continue
}
insert := parseSingleTypeString(p)

if insert.ParameterType != "" {
ret = append(ret, insert)
}
}

return returnType, ret, isConst, isNoExcept, nil
return returnType, ret, isConst, noexceptExpr, nil
}

// tokenizeMultipleParameters is like strings.Split by comma, except it does not
Expand Down
35 changes: 23 additions & 12 deletions cmd/genbindings/clang2il_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,48 @@ import (

func TestParseMethodTypes(t *testing.T) {
type testCase struct {
input string
expectReturn CppParameter
expectParams []CppParameter
expectErr bool
input string
expectReturn CppParameter
expectParams []CppParameter
expectNoexcept string
expectErr bool
}

cases := []testCase{
testCase{
{
input: "void (bool)",
expectReturn: CppParameter{ParameterType: "void"},
expectParams: []CppParameter{
CppParameter{ParameterType: "bool"},
{ParameterType: "bool"},
},
},
testCase{
{
input: "bool (QList<QPair<Foo, Bar>>, QString)",
expectReturn: CppParameter{ParameterType: "bool"},
expectParams: []CppParameter{
CppParameter{ParameterType: "QList<QPair<Foo, Bar>>"},
CppParameter{ParameterType: "QString"},
{ParameterType: "QList<QPair<Foo, Bar>>"},
{ParameterType: "QString"},
},
// expectErr: true,
},
testCase{
{
input: "bool (QList<QWidget*>)",
expectReturn: CppParameter{ParameterType: "bool"},
expectParams: []CppParameter{
CppParameter{ParameterType: "QList<QWidget*>"},
{ParameterType: "QList<QWidget*>"},
},
},

{
input: "void () noexcept(Data::CanBeSmall)",
expectReturn: CppParameter{ParameterType: "void"},
expectParams: []CppParameter{},
expectNoexcept: "noexcept(Data::CanBeSmall)",
},
}

for _, tc := range cases {
r, p, _ /* isConst */, _ /* isNoExcept */, err := parseTypeString(tc.input)
r, p, _ /* isConst */, noexcept, err := parseTypeString(tc.input)

if tc.expectErr {
if err == nil {
Expand All @@ -60,6 +68,9 @@ func TestParseMethodTypes(t *testing.T) {
if !reflect.DeepEqual(p, tc.expectParams) {
t.Errorf("Test %q\n-got params=%#v\n-expected =%#v", tc.input, p, tc.expectParams)
}
if !reflect.DeepEqual(noexcept, tc.expectNoexcept) {
t.Errorf("Test %q\n-got noexcept=%#v\n-noexcept =%#v", tc.input, noexcept, tc.expectNoexcept)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/genbindings/emitcabi.go
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ extern "C" {

ret.WriteString(
"\t// Subclass to allow providing a Go implementation\n" +
"\tvirtual " + m.ReturnType.RenderTypeQtCpp() + " " + m.CppCallTarget() + "(" + emitParametersCpp(m) + ") " + ifv(m.IsConst, "const ", "") + ifv(m.IsNoExcept, "noexcept ", "") + "override {\n",
"\tvirtual " + m.ReturnType.RenderTypeQtCpp() + " " + m.CppCallTarget() + "(" + emitParametersCpp(m) + ") " + ifv(m.IsConst, "const ", "") + ifv(len(m.Noexcept) > 0, m.Noexcept+" ", "") + "override {\n",
)

ret.WriteString("\t\tif (" + handleVarname + " == 0) {\n")
Expand Down
2 changes: 1 addition & 1 deletion cmd/genbindings/intermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ type CppMethod struct {
IsStatic bool
IsSignal bool
IsConst bool
IsNoExcept bool
Noexcept string
IsVariable bool
IsVirtual bool
IsPureVirtual bool // Virtual method was declared with = 0 i.e. there is no base method here to call
Expand Down
2 changes: 1 addition & 1 deletion cmd/genbindings/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func generate(packageName string, srcDirs []string, allowHeaderFn func(string) b
astTransformApplyQuirks(packageName, parsed) // must be before optional/overload expansion
astTransformOptional(parsed)
astTransformOverloads(parsed)
astTransformConstructorOrder(parsed)
astTransformConstructorOrder(packageName, parsed)
atr.Process(parsed)

// Update global state tracker (AFTER astTransformChildClasses)
Expand Down
17 changes: 15 additions & 2 deletions cmd/genbindings/transformctors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

// astTransformConstructorOrder creates a canonical ordering for constructors.
func astTransformConstructorOrder(parsed *CppParsedHeader) {
func astTransformConstructorOrder(packageName string, parsed *CppParsedHeader) {

// QFoo(QWidget* parent);
checkIsDefaultCtor := func(candidate *CppMethod) bool {
Expand Down Expand Up @@ -53,7 +53,20 @@ func astTransformConstructorOrder(parsed *CppParsedHeader) {
return true
}

// Case 3: Normal
// Case 3: Qt 5 QDateTime({no args}) moved to end
// This softens a compatibility break when noexcept attribute
// parsing was added
// @ref https://github.com/mappu/miqt/pull/305
if packageName == "qt" && c.ClassName == "QDateTime" {
ic = (len(c.Ctors[i].Parameters) == 0)
jc = (len(c.Ctors[j].Parameters) == 0)

if !ic && jc {
return true
}
}

// Default case: Normal
return false
})

Expand Down
12 changes: 12 additions & 0 deletions qt/gen_qcommandlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ struct miqt_array /* of struct miqt_string */ QCommandLineParser_unknownOptionN
return _out;
}

void QCommandLineParser_showVersion(QCommandLineParser* self) {
self->showVersion();
}

void QCommandLineParser_showHelp(QCommandLineParser* self) {
self->showHelp();
}

struct miqt_string QCommandLineParser_helpText(const QCommandLineParser* self) {
QString _ret = self->helpText();
// Convert QString from UTF-16 in C++ RAII memory to UTF-8 in manually-managed C memory
Expand Down Expand Up @@ -330,6 +338,10 @@ void QCommandLineParser_addPositionalArgument2(QCommandLineParser* self, struct
self->addPositionalArgument(name_QString, description_QString, syntax_QString);
}

void QCommandLineParser_showHelpWithExitCode(QCommandLineParser* self, int exitCode) {
self->showHelp(static_cast<int>(exitCode));
}

void QCommandLineParser_delete(QCommandLineParser* self) {
delete self;
}
Expand Down
12 changes: 12 additions & 0 deletions qt/gen_qcommandlineparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ func (this *QCommandLineParser) UnknownOptionNames() []string {
return _ret
}

func (this *QCommandLineParser) ShowVersion() {
C.QCommandLineParser_showVersion(this.h)
}

func (this *QCommandLineParser) ShowHelp() {
C.QCommandLineParser_showHelp(this.h)
}

func (this *QCommandLineParser) HelpText() string {
var _ms C.struct_miqt_string = C.QCommandLineParser_helpText(this.h)
_ret := C.GoStringN(_ms.data, C.int(int64(_ms.len)))
Expand Down Expand Up @@ -353,6 +361,10 @@ func (this *QCommandLineParser) AddPositionalArgument2(name string, description
C.QCommandLineParser_addPositionalArgument2(this.h, name_ms, description_ms, syntax_ms)
}

func (this *QCommandLineParser) ShowHelpWithExitCode(exitCode int) {
C.QCommandLineParser_showHelpWithExitCode(this.h, (C.int)(exitCode))
}

// Delete this object from C++ memory.
func (this *QCommandLineParser) Delete() {
C.QCommandLineParser_delete(this.h)
Expand Down
3 changes: 3 additions & 0 deletions qt/gen_qcommandlineparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@ struct miqt_array /* of struct miqt_string */ QCommandLineParser_valuesWithOpti
struct miqt_array /* of struct miqt_string */ QCommandLineParser_positionalArguments(const QCommandLineParser* self);
struct miqt_array /* of struct miqt_string */ QCommandLineParser_optionNames(const QCommandLineParser* self);
struct miqt_array /* of struct miqt_string */ QCommandLineParser_unknownOptionNames(const QCommandLineParser* self);
void QCommandLineParser_showVersion(QCommandLineParser* self);
void QCommandLineParser_showHelp(QCommandLineParser* self);
struct miqt_string QCommandLineParser_helpText(const QCommandLineParser* self);
struct miqt_string QCommandLineParser_tr2(const char* sourceText, const char* disambiguation);
struct miqt_string QCommandLineParser_tr3(const char* sourceText, const char* disambiguation, int n);
struct miqt_string QCommandLineParser_trUtf82(const char* sourceText, const char* disambiguation);
struct miqt_string QCommandLineParser_trUtf83(const char* sourceText, const char* disambiguation, int n);
void QCommandLineParser_addPositionalArgument2(QCommandLineParser* self, struct miqt_string name, struct miqt_string description, struct miqt_string syntax);
void QCommandLineParser_showHelpWithExitCode(QCommandLineParser* self, int exitCode);

void QCommandLineParser_delete(QCommandLineParser* self);

Expand Down
4 changes: 4 additions & 0 deletions qt/gen_qdatetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,10 @@ QDateTime* QDateTime_new6(QDate* param1, QTime* param2, int spec) {
return new (std::nothrow) QDateTime(*param1, *param2, static_cast<Qt::TimeSpec>(spec));
}

QDateTime* QDateTime_new7() {
return new (std::nothrow) QDateTime();
}

void QDateTime_operatorAssign(QDateTime* self, QDateTime* other) {
self->operator=(*other);
}
Expand Down
6 changes: 6 additions & 0 deletions qt/gen_qdatetime.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,12 @@ func NewQDateTime6(param1 *QDate, param2 *QTime, spec TimeSpec) *QDateTime {
return newQDateTime(C.QDateTime_new6(param1.cPointer(), param2.cPointer(), (C.int)(spec)))
}

// NewQDateTime7 constructs a new QDateTime object.
func NewQDateTime7() *QDateTime {

return newQDateTime(C.QDateTime_new7())
}

func (this *QDateTime) OperatorAssign(other *QDateTime) {
C.QDateTime_operatorAssign(this.h, other.cPointer())
}
Expand Down
1 change: 1 addition & 0 deletions qt/gen_qdatetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ QDateTime* QDateTime_new3(QDate* date, QTime* time, int spec, int offsetSeconds)
QDateTime* QDateTime_new4(QDate* date, QTime* time, QTimeZone* timeZone);
QDateTime* QDateTime_new5(QDateTime* other);
QDateTime* QDateTime_new6(QDate* param1, QTime* param2, int spec);
QDateTime* QDateTime_new7();
void QDateTime_operatorAssign(QDateTime* self, QDateTime* other);
void QDateTime_swap(QDateTime* self, QDateTime* other);
bool QDateTime_isNull(const QDateTime* self);
Expand Down
4 changes: 4 additions & 0 deletions qt/gen_qitemselectionmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,10 @@ QItemSelection* QItemSelection_new2(QModelIndex* topLeft, QModelIndex* bottomRig
return new (std::nothrow) QItemSelection(*topLeft, *bottomRight);
}

QItemSelection* QItemSelection_new3(QItemSelection* param1) {
return new (std::nothrow) QItemSelection(*param1);
}

void QItemSelection_select(QItemSelection* self, QModelIndex* topLeft, QModelIndex* bottomRight) {
self->select(*topLeft, *bottomRight);
}
Expand Down
6 changes: 6 additions & 0 deletions qt/gen_qitemselectionmodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,12 @@ func NewQItemSelection2(topLeft *QModelIndex, bottomRight *QModelIndex) *QItemSe
return newQItemSelection(C.QItemSelection_new2(topLeft.cPointer(), bottomRight.cPointer()))
}

// NewQItemSelection3 constructs a new QItemSelection object.
func NewQItemSelection3(param1 *QItemSelection) *QItemSelection {

return newQItemSelection(C.QItemSelection_new3(param1.cPointer()))
}

func (this *QItemSelection) Select(topLeft *QModelIndex, bottomRight *QModelIndex) {
C.QItemSelection_select(this.h, topLeft.cPointer(), bottomRight.cPointer())
}
Expand Down
Loading