Skip to content

Add ClassBuilder::add_static_ivar #233

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

Merged
merged 2 commits into from
Aug 1, 2022
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
1 change: 1 addition & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
(default) feature flag `"foundation"`.
* Added `declare_class!`, `extern_class!` and `ns_string!` macros from
`objc2-foundation`.
* Added helper method `ClassBuilder::add_static_ivar`.

### Changed
* **BREAKING**: Change selector syntax in `declare_class!` macro to be more Rust-like.
Expand Down
2 changes: 1 addition & 1 deletion objc2/examples/class_with_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl<'a> MyObject<'a> {
let superclass = NSObject::class();
let mut builder = ClassBuilder::new("MyObject", superclass).unwrap();

builder.add_ivar::<<NumberIvar<'a> as IvarType>::Type>(<NumberIvar<'a>>::NAME);
builder.add_static_ivar::<NumberIvar<'a>>();

/// Helper struct since we can't access the instance variable
/// from inside MyObject, since it hasn't been initialized yet!
Expand Down
14 changes: 13 additions & 1 deletion objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,11 @@ impl ClassBuilder {

/// Adds an ivar with type `T` and the provided name.
///
///
/// # Panics
///
/// If the ivar wasn't successfully added.
/// If the ivar wasn't successfully added for some reason - this usually
/// happens if there already was an ivar with that name.
pub fn add_ivar<T: Encode>(&mut self, name: &str) {
let c_name = CString::new(name).unwrap();
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
Expand All @@ -422,6 +424,16 @@ impl ClassBuilder {
assert!(success.as_bool(), "Failed to add ivar {}", name);
}

/// Adds an instance variable from an [`IvarType`].
///
///
/// # Panics
///
/// Same as [`ClassBuilder::add_ivar`].
pub fn add_static_ivar<T: IvarType>(&mut self) {
self.add_ivar::<T::Type>(T::NAME);
}

/// Adds the given protocol to self.
///
/// # Panics
Expand Down
53 changes: 36 additions & 17 deletions objc2/src/declare/ivar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,14 @@ unsafe impl<T: IvarType> IvarType for MaybeUninit<T> {
/// particular, this is never safe to have on the stack by itself.
///
/// Additionally, the instance variable described by `T` must be available on
/// the specific instance, and be of the exact same type.
/// the specific instance, and be of the exact same type. When declaring the
/// object yourself, you can ensure this using
/// [`ClassBuilder::add_static_ivar`].
///
/// Finally, two ivars with the same name must not be used on the same object.
///
/// [`ClassBuilder::add_static_ivar`]: crate::declare::ClassBuilder::add_static_ivar
///
///
/// # Examples
///
Expand Down Expand Up @@ -106,7 +110,7 @@ unsafe impl<T: IvarType> IvarType for MaybeUninit<T> {
/// # use objc2::declare::ClassBuilder;
/// # let mut builder = ClassBuilder::new("MyObject", class!(NSObject)).unwrap();
/// // Declare the class and add the instance variable to it
/// builder.add_ivar::<<MyCustomIvar as IvarType>::Type>(MyCustomIvar::NAME);
/// builder.add_static_ivar::<MyCustomIvar>();
/// # let _cls = builder.register();
///
/// let obj: MyObject;
Expand All @@ -123,11 +127,8 @@ pub struct Ivar<T: IvarType> {
item: PhantomData<T::Type>,
}

impl<T: IvarType> Deref for Ivar<T> {
type Target = T::Type;

#[inline]
fn deref(&self) -> &Self::Target {
impl<T: IvarType> Ivar<T> {
fn get_ref(&self) -> &T::Type {
// SAFETY: The user ensures that this is placed in a struct that can
// be reinterpreted as an `Object`. Since `Ivar` can never be
// constructed by itself (and is neither Copy nor Clone), we know that
Expand All @@ -146,13 +147,10 @@ impl<T: IvarType> Deref for Ivar<T> {
// exists and has the correct type
unsafe { obj.ivar::<T::Type>(T::NAME) }
}
}

impl<T: IvarType> DerefMut for Ivar<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
fn get_mut_ptr(&mut self) -> *mut T::Type {
let ptr = NonNull::from(self).cast::<Object>();
// SAFETY: Same as `deref`.
// SAFETY: Same as `get_ref`.
//
// Note: We don't use `mut` because the user might have two mutable
// references to different ivars, as such:
Expand All @@ -177,12 +175,33 @@ impl<T: IvarType> DerefMut for Ivar<T> {
// this is definitely safe.
let obj = unsafe { ptr.as_ref() };

// SAFETY: Same as `deref`
//
// Safe as mutable because there is only one access to a particular
// ivar at a time (since we have `&mut self`). `Object` is
// SAFETY: User ensures that the `Ivar<T>` is only used when the ivar
// exists and has the correct type
unsafe { obj.ivar_ptr::<T::Type>(T::NAME) }
}

#[inline]
fn get_mut(&mut self) -> &mut T::Type {
// SAFETY: Safe as mutable because there is only one access to a
// particular ivar at a time (since we have `&mut self`). `Object` is
// `UnsafeCell`, so mutable access through `&Object` is allowed.
unsafe { obj.ivar_ptr::<T::Type>(T::NAME).as_mut().unwrap_unchecked() }
unsafe { self.get_mut_ptr().as_mut().unwrap_unchecked() }
}
}

impl<T: IvarType> Deref for Ivar<T> {
type Target = T::Type;

#[inline]
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}

impl<T: IvarType> DerefMut for Ivar<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}

Expand Down
8 changes: 3 additions & 5 deletions objc2/src/macros/declare_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,7 @@ macro_rules! declare_class {
unsafe $v struct $name<>: $inherits, $($inheritance_rest,)* $crate::runtime::Object {
// SAFETY:
// - The ivars are in a type used as an Objective-C object.
// - The instance variable is defined in the exact same manner
// in `class` below.
// - The ivar is added to the class below.
// - Rust prevents having two fields with the same name.
// - Caller upholds that the ivars are properly initialized.
$($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)*
Expand Down Expand Up @@ -527,10 +526,9 @@ macro_rules! declare_class {
);
let mut builder = $crate::declare::ClassBuilder::new(stringify!($name), superclass).expect(err_str);

// Ivars
$(
builder.add_ivar::<<$ivar as $crate::declare::IvarType>::Type>(
<$ivar as $crate::declare::IvarType>::NAME
);
builder.add_static_ivar::<$ivar>();
)*

$(
Expand Down