Skip to content

Commit

Permalink
Merge pull request #233 from madsmtm/add-static-ivar
Browse files Browse the repository at this point in the history
Add `ClassBuilder::add_static_ivar`
  • Loading branch information
madsmtm authored Aug 1, 2022
2 parents e5919bb + d9f9b32 commit 6f68cd0
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 24 deletions.
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

0 comments on commit 6f68cd0

Please sign in to comment.