Skip to content

Use Bindgen names in Object-C pointer input and return types. #1835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
simlay opened this issue Jul 25, 2020 · 1 comment · Fixed by #1847
Closed

Use Bindgen names in Object-C pointer input and return types. #1835

simlay opened this issue Jul 25, 2020 · 1 comment · Fixed by #1847

Comments

@simlay
Copy link
Contributor

simlay commented Jul 25, 2020

I've been playing around with the generated bindings for UIKit and have noticed a point of friction that I think we could alleviate with bindgen but want to get some feedback before I implement it.

Input ObjC Header

// bindgen-flags: --objc-extern-crate -- -x objective-c
// bindgen-osx-only

@interface Bar
@end

@interface Foo
+ (Bar*)methodReturningBar;
- (void)methodUsingBar:(Bar *)my_bar;
@end

Bindgen Invocation

$ cargo run -- objc-pointer-test.h -- -x objective-c

Actual Results

use objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Bar(pub id);
impl std::ops::Deref for Bar {
    type Target = objc::runtime::Object;
    fn deref(&self) -> &Self::Target {
        unsafe { &*self.0 }
    }
}
unsafe impl objc::Message for Bar {}
impl Bar {
    pub fn alloc() -> Self {
        Self(unsafe { msg_send!(objc::class!(Bar), alloc) })
    }
}
impl IBar for Bar {}
pub trait IBar: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
    type Target = objc::runtime::Object;
    fn deref(&self) -> &Self::Target {
        unsafe { &*self.0 }
    }
}
unsafe impl objc::Message for Foo {}
impl Foo {
    pub fn alloc() -> Self {
        Self(unsafe { msg_send!(objc::class!(Foo), alloc) })
    }
}
impl IFoo for Foo {}
pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodUsingBar_(self, my_bar: id)
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodUsingBar: my_bar)
    }
    unsafe fn methodReturningBar() -> *mut objc::runtime::Object
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(class!(Foo), methodReturningBar)
    }
}

The part of this generated binding that's relevant to this ticket is:

pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodUsingBar_(self, my_bar: id)
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodUsingBar: my_bar)
    }
    unsafe fn methodReturningBar() -> *mut objc::runtime::Object
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(class!(Foo), methodReturningBar)
    }
}

Expected Results

There's a lot of other stuff generated but this is the change I'm suggesting.

pub struct Bar(pub id); // This is the same as above. It's here to clarify Bar
pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodUsingBar_(self, my_bar: Bar)
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodUsingBar: *my_bar)
    }
    unsafe fn methodReturningBar() -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        Bar(msg_send!(class!(Foo), methodReturningBar))
    }
}

In case it's not clear as to why this is useful, I'll provide an example. In my case, I've got some uikit-sys bindings and want to create a UILabel and give it a boarder. With the current bindings you do the following:;

let label = UILabel::alloc();
label.init();
// label.layer() returns a CALayer*
// https://developer.apple.com/documentation/uikit/uiview/1622436-layer?language=objc
let layer = CALayer(label.layer());
layer.setBorderWidth_(3.0);

If we (probably me) adds this feature we will have something more like:

let label = UILabel::alloc();
label.init();
// layer is of type CALayer
let layer = label.layer();
layer.setBorderWidth_(3.0);

This might seem like a small reduction in code but the area to where it's very useful is type checking. Given the message passing nature of objective-c, you could create CALayer with any Objective-c id and the bindings that are currently generated will accept this at compile time. Adding a generated wrapper around the objective-c id's will add better checking as well as the user of bindings will have a much easier time navigating the creation and usage of these bindings because return of functions will actually inform the user of the rust side types and would auto link to that types documentation.

Thoughts?

@simlay simlay changed the title Use Objective-C Bindgen names in Object-C pointer return types. Use Objective-C Bindgen names in Object-C pointer input and return types. Jul 25, 2020
@simlay simlay changed the title Use Objective-C Bindgen names in Object-C pointer input and return types. Use Bindgen names in Object-C pointer input and return types. Jul 25, 2020
@emilio
Copy link
Contributor

emilio commented Jul 27, 2020

Returning more strongly typed things sounds useful to me, for what is worth, if we can get that information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants