diff --git a/spec/integration/qt.cpp b/spec/integration/qt.cpp index 1ab7a4a..5d3b7cb 100644 --- a/spec/integration/qt.cpp +++ b/spec/integration/qt.cpp @@ -20,6 +20,21 @@ 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 @@ -27,6 +42,7 @@ class SomeObject { Q_OBJECT public: int normalMethod() { return 1; } + Conv convMethod() { return { }; } signals: void stuffHappened() { @@ -48,6 +64,10 @@ class SomeObject { void privateSignal(QPrivateSignal) { // Empty. } + + void convSignal(const Conv &) { + // Empty. + } }; // Tests Q_GADGET cleaning diff --git a/spec/integration/qt.yml b/spec/integration/qt.yml index 14709f3..adf22ae 100644 --- a/spec/integration/qt.yml +++ b/spec/integration/qt.yml @@ -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 diff --git a/spec/integration/qt_spec.cr b/spec/integration/qt_spec.cr index 9e1ea7f..785bcff 100644 --- a/spec/integration/qt_spec.cr +++ b/spec/integration/qt_spec.cr @@ -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 diff --git a/src/bindgen/call_builder/cpp_qobject_connect.cr b/src/bindgen/call_builder/cpp_qobject_connect.cr index 5d8b17b..a984e1f 100644 --- a/src/bindgen/call_builder/cpp_qobject_connect.cr +++ b/src/bindgen/call_builder/cpp_qobject_connect.cr @@ -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 diff --git a/src/bindgen/call_builder/cpp_to_crystal_proc.cr b/src/bindgen/call_builder/cpp_to_crystal_proc.cr index 41f3c23..0080f69 100644 --- a/src/bindgen/call_builder/cpp_to_crystal_proc.cr +++ b/src/bindgen/call_builder/cpp_to_crystal_proc.cr @@ -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) @@ -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 diff --git a/src/bindgen/crystal/pass.cr b/src/bindgen/crystal/pass.cr index 2bcbef2..af3681c 100644 --- a/src/bindgen/crystal/pass.cr +++ b/src/bindgen/crystal/pass.cr @@ -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 diff --git a/src/bindgen/processor/qt.cr b/src/bindgen/processor/qt.cr index 8fb5107..9a4f337 100644 --- a/src/bindgen/processor/qt.cr +++ b/src/bindgen/processor/qt.cr @@ -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), diff --git a/src/bindgen/processor/virtual_override.cr b/src/bindgen/processor/virtual_override.cr index 850fa4a..adfe740 100644 --- a/src/bindgen/processor/virtual_override.cr +++ b/src/bindgen/processor/virtual_override.cr @@ -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