Skip to content
This repository was archived by the owner on Feb 15, 2026. It is now read-only.
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
20 changes: 20 additions & 0 deletions spec/integration/qt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,29 @@ struct QObject {
}
};

// Test object conversion at Proc boundaries
struct Conv {
};

struct ConvCpp {
};

ConvCpp conv_from_cpp(const Conv &) {
return { };
}

Conv conv_to_cpp(const ConvCpp &) {
return { };
}

// On to the actual test classes:

// Tests signal/slots connection wrapping
class SomeObject {
Q_OBJECT
public:
int normalMethod() { return 1; }
Conv convMethod() { return { }; }

signals:
void stuffHappened() {
Expand All @@ -48,6 +64,10 @@ class SomeObject {
void privateSignal(QPrivateSignal) {
// Empty.
}

void convSignal(const Conv &) {
// Empty.
}
};

// Tests Q_GADGET cleaning
Expand Down
13 changes: 13 additions & 0 deletions spec/integration/qt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,22 @@ classes:
"QMetaObject::Connection": SignalConnection
SomeObject: SomeObject
SomeGadget: SomeGadget
Conv: Conv

types:
"QMetaObject::Connection":
crystal_type: SignalConnection
cpp_type: "QMetaObject::Connection"
binding_type: QMetaObjectConnection
Conv:
crystal_type: ConvCrystal
cpp_type: ConvCpp
binding_type: ConvBinding
to_cpp: "conv_to_cpp(%)"
from_cpp: "conv_from_cpp(%)"
to_crystal: "BindgenHelper.conv_to_crystal(%)"
from_crystal: "BindgenHelper.conv_from_crystal(%)"
kind: Struct
builtin: true
pass_by: Value
wrapper_pass_by: Value
20 changes: 20 additions & 0 deletions spec/integration/qt_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ require "./spec_helper"
describe "Qt-specific wrapper features" do
it "works" do
build_and_run("qt") do
module Test
lib Binding
struct ConvBinding
x : Int32
end
end

alias ConvBinding = Binding::ConvBinding

module BindgenHelper
def conv_to_crystal(x : Binding::ConvBinding) : ConvCrystal
ConvCrystal.new
end

def conv_from_crystal(x : ConvCrystal) : Binding::ConvBinding
Binding::ConvBinding.new x: 0
end
end
end

context "signal behaviour" do
it "creates a on_X method" do
subject = Test::SomeObject.new
Expand Down
11 changes: 6 additions & 5 deletions src/bindgen/call_builder/cpp_qobject_connect.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ module Bindgen
end

class Body < Call::Body
def initialize(@proc : Call)
def initialize(@lambda : Call)
end

def to_code(call : Call, platform : Graph::Platform) : String
formatter = Cpp::Format.new
ptr = formatter.function_pointer(@proc)
lambda_args = formatter.argument_list(call.arguments)

inner = @proc.body.to_code(@proc, platform)
code = %[QObject::connect(_self_, (#{ptr})&#{call.name}, [_proc_](#{lambda_args}){ #{inner}; })]
lambda_body = @lambda.body.to_code(@lambda, platform)
ptr = formatter.function_pointer(@lambda)

code = %[QObject::connect(_self_, (#{ptr})&#{call.name}, #{lambda_body})]

call.result.apply_conversion(code)
end
end
Expand Down
25 changes: 22 additions & 3 deletions src/bindgen/call_builder/cpp_to_crystal_proc.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ module Bindgen
end

# Calls the *method*, using the *proc_name* to call-through to Crystal.
def build(method : Parser::Method, proc_name : String = "_proc_") : Call
# If *lambda* is true, instead of invoking the *method*, builds a C++
# lambda expression that wraps the invocation.
def build(method : Parser::Method, *, proc_name : String = "_proc_", lambda = false) : Call
pass = Cpp::Pass.new(@db)

arguments = pass.arguments_from_cpp(method.arguments)
Expand All @@ -18,17 +20,34 @@ module Bindgen
name: proc_name,
result: result,
arguments: arguments,
body: Body.new,
body: lambda ? LambdaBody.new : InvokeBody.new,
)
end

class Body < Call::Body
# Method invocation.
class InvokeBody < Call::Body
def to_code(call : Call, platform : Graph::Platform) : String
pass_args = call.arguments.map(&.call).join(", ")
code = %[#{call.name}(#{pass_args})]
call.result.apply_conversion(code)
end
end

# Lambda expression. Captures the `CrystalProc` by value (therefore it
# is *not* convertible to a C function pointer).
class LambdaBody < Call::Body
def to_code(call : Call, platform : Graph::Platform) : String
formatter = Cpp::Format.new

lambda_args = formatter.argument_list(call.arguments)
pass_args = call.arguments.map(&.call).join(", ")
inner = %[#{call.name}(#{pass_args})]

prefix = "return " unless call.result.type.pure_void?

"[#{call.name}](#{lambda_args}){ #{prefix}#{inner}; }"
end
end
end
end
end
7 changes: 6 additions & 1 deletion src/bindgen/crystal/pass.cr
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,18 @@ module Bindgen
is_ref, ptr = reconfigure_pass_type(rules.pass_by, is_ref, ptr)
end

if type.kind.function?
template = Template::ProcFromWrapper.new(type, @db).followed_by(template)
end

ptr += 1 if is_ref # Translate reference to pointer
is_ref = false
{is_ref, ptr, type_name, template, false}
end
end

# Builds the type-name of a Crystal `Proc`, from the function *type*.
# Builds the type-name of a Crystal `Proc` for Crystal wrappers, from the
# function *type*.
private def proc_to_wrapper(type)
args = type.template.not_nil!.arguments

Expand Down
2 changes: 1 addition & 1 deletion src/bindgen/processor/qt.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module Bindgen
call = CallBuilder::CppQobjectConnect.new(@db)
wrapper = CallBuilder::CppWrapper.new(@db)

proc = to_proc.build(method)
proc = to_proc.build(method, lambda: true)
connector.calls[Graph::Platform::Cpp] = wrapper.build(
method: connector.origin,
target: call.build(method, proc),
Expand Down
2 changes: 1 addition & 1 deletion src/bindgen/processor/virtual_override.cr
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ module Bindgen
method: method,
class_name: class_name,
target: target,
virtual_target: to_crystal.build(method, proc_name),
virtual_target: to_crystal.build(method, proc_name: proc_name),
in_superclass: in_superclass,
)
end
Expand Down