diff --git a/README.md b/README.md
index 65d02081d..f0d23f3a9 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,13 @@
# [![Rust + \[Obj-C\]](assets/logo-small.png)](https://github.com/madsmtm/objc2) Objective-C in Rust
[](../LICENSE.txt)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
# DISCLAIMER! These crates are work in progress, and should not be used in production environments. Use the battle-tested `objc` family instead!
+Anyway, thanks for being here, any help testing things out is highly
+appreciated!
+
## Migrating from original crates
diff --git a/block-sys/README.md b/block-sys/README.md
index dea21e567..3d0d1a5ce 100644
--- a/block-sys/README.md
+++ b/block-sys/README.md
@@ -3,11 +3,14 @@
[](https://crates.io/crates/block-sys)
[](../LICENSE.txt)
[](https://docs.rs/block-sys/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Raw Rust bindings to Apple's C language extension of blocks.
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
+
+
## Runtime Support
This library is basically just a raw interface to the aptly specified [Blocks
@@ -17,7 +20,9 @@ several different helper functions), the most important aspect being that the
libraries are named differently, so the linking must take that into account.
The user can choose the desired runtime by using the relevant cargo feature
-flags, see the following sections:
+flags, see the following sections. Note that if the `objc-sys` crate is
+present in the module tree, this should have the same feature flag enabled as
+that.
### Apple's [`libclosure`](https://opensource.apple.com/source/libclosure/)
@@ -52,6 +57,8 @@ and is now used in [Swift's `libdispatch`] and [Swift's Foundation] as well.
This can be easily used on many Linux systems with the `libblocksruntime-dev`
package.
+Using this runtime probably won't work together with `objc-sys` crate.
+
[Swift's `libdispatch`]: https://github.com/apple/swift-corelibs-libdispatch/tree/swift-5.5.1-RELEASE/src/BlocksRuntime
[Swift's Foundation]: https://github.com/apple/swift-corelibs-foundation/tree/swift-5.5.1-RELEASE/Sources/BlocksRuntime
diff --git a/block-sys/src/lib.rs b/block-sys/src/lib.rs
index b7b51df01..ac2ed7d3c 100644
--- a/block-sys/src/lib.rs
+++ b/block-sys/src/lib.rs
@@ -4,7 +4,8 @@
//! sources, but the [ABI specification][ABI] is really the place you should
//! be looking!
//!
-//! See also the `README.md` for more info.
+//! See also the [`README.md`](https://crates.io/crates/block-sys) for more
+//! background information, and for how to configure the desired runtime.
//!
//! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html
@@ -257,7 +258,7 @@ pub struct Block_layout {
/// space on the stack allocated to hold the return value.
pub invoke: Option,
/// The block's descriptor. The actual type of this is:
- /// ```ignore
+ /// ```pseudo-code
/// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) {
/// (false, false) => Block_descriptor_header,
/// (true, false) => Block_descriptor,
diff --git a/block2/CHANGELOG.md b/block2/CHANGELOG.md
index 60b100c35..55c7ce66d 100644
--- a/block2/CHANGELOG.md
+++ b/block2/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
+### Changed
+* Changed `global_block!` macro to take an optional semicolon at the end.
+* Improved documentation.
+
## 0.2.0-alpha.2 - 2021-12-22
diff --git a/block2/README.md b/block2/README.md
index 042fda6a5..3d9039979 100644
--- a/block2/README.md
+++ b/block2/README.md
@@ -3,50 +3,14 @@
[](https://crates.io/crates/block2)
[](../LICENSE.txt)
[](https://docs.rs/block2/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Apple's C language extension of blocks in Rust.
-For more information on the specifics of the block implementation, see
-Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html
+This crate provides functionality for interracting with C blocks, effectively
+the C-equivalent of Rust's closures.
-## Invoking blocks
+See [the docs](https://docs.rs/block2/) for a more thorough overview.
-The `Block` struct is used for invoking blocks from Objective-C. For example,
-consider this Objective-C function:
-
-```objc
-int32_t sum(int32_t (^block)(int32_t, int32_t)) {
- return block(5, 8);
-}
-```
-
-We could write it in Rust as the following:
-
-```rust
-use block2::Block;
-unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 {
- block.call((5, 8))
-}
-```
-
-Note the extra parentheses in the `call` method, since the arguments must be
-passed as a tuple.
-
-## Creating blocks
-
-Creating a block to pass to Objective-C can be done with the `ConcreteBlock`
-struct. For example, to create a block that adds two `i32`s, we could write:
-
-```rust
-use block2::ConcreteBlock;
-let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
-let block = block.copy();
-assert_eq!(unsafe { block.call((5, 8)) }, 13);
-```
-
-It is important to copy your block to the heap (with the `copy` method) before
-passing it to Objective-C; this is because our `ConcreteBlock` is only meant
-to be copied once, and we can enforce this in Rust, but if Objective-C code
-were to copy it twice we could have a double free.
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
diff --git a/block2/src/global.rs b/block2/src/global.rs
index b653d28ec..de81064a2 100644
--- a/block2/src/global.rs
+++ b/block2/src/global.rs
@@ -99,8 +99,8 @@ where
/// global_block! {
/// static MY_BLOCK = || -> i32 {
/// 42
-/// }
-/// };
+/// };
+/// }
/// assert_eq!(unsafe { MY_BLOCK.call(()) }, 42);
/// ```
///
@@ -109,8 +109,8 @@ where
/// global_block! {
/// static ADDER_BLOCK = |x: i32, y: i32| -> i32 {
/// x + y
-/// }
-/// };
+/// };
+/// }
/// assert_eq!(unsafe { ADDER_BLOCK.call((5, 7)) }, 12);
/// ```
///
@@ -119,8 +119,8 @@ where
/// global_block! {
/// pub static MUTATING_BLOCK = |x: &mut i32| {
/// *x = *x + 42;
-/// }
-/// };
+/// };
+/// }
/// let mut x = 5;
/// unsafe { MUTATING_BLOCK.call((&mut x,)) };
/// assert_eq!(x, 47);
@@ -131,8 +131,19 @@ where
/// ```compile_fail
/// use block2::global_block;
/// global_block! {
-/// pub static INVALID_BLOCK = |b: Box| {}
-/// };
+/// pub static BLOCK = |b: Box| {};
+/// }
+/// ```
+///
+/// There is also no way to get a block function that's generic over it's
+/// arguments. One could imagine the following syntax would work, but it can't
+/// due to implementation limitations:
+///
+/// ```compile_fail
+/// use block2::global_block;
+/// global_block! {
+/// pub static BLOCK = |b: T| {};
+/// }
/// ```
///
/// [`Box`]: std::boxed::Box
@@ -141,20 +152,23 @@ macro_rules! global_block {
// `||` is parsed as one token
(
$(#[$m:meta])*
- $vis:vis static $name:ident = || $(-> $r:ty)? $body:block
+ $vis:vis static $name:ident = || $(-> $r:ty)? $body:block $(;)?
) => {
- $crate::global_block!($(#[$m])* $vis static $name = |,| $(-> $r)? $body);
+ $crate::global_block!(
+ $(#[$m])*
+ $vis static $name = |,| $(-> $r)? $body
+ );
};
(
$(#[$m:meta])*
- $vis:vis static $name:ident = |$($a:ident: $t:ty),* $(,)?| $(-> $r:ty)? $body:block
+ $vis:vis static $name:ident = |$($a:ident: $t:ty),* $(,)?| $(-> $r:ty)? $body:block $(;)?
) => {
$(#[$m])*
#[allow(unused_unsafe)]
$vis static $name: $crate::GlobalBlock<($($t,)*) $(, $r)?> = unsafe {
let mut layout = $crate::GlobalBlock::<($($t,)*) $(, $r)?>::__DEFAULT_LAYOUT;
layout.isa = &$crate::ffi::_NSConcreteGlobalBlock as *const _ as *mut _;
- layout.invoke = Some({
+ layout.invoke = ::core::option::Option::Some({
unsafe extern "C" fn inner(_: *mut $crate::ffi::Block_layout, $($a: $t),*) $(-> $r)? {
$body
}
@@ -172,7 +186,7 @@ macro_rules! global_block {
mod tests {
global_block! {
/// Test comments and visibility
- pub(super) static NOOP_BLOCK = || {}
+ pub(super) static NOOP_BLOCK = || {};
}
global_block! {
@@ -180,7 +194,7 @@ mod tests {
#[allow(unused)]
static BLOCK = |x: i32, y: i32, z: i32, w: i32,| -> i32 {
x + y + z + w
- }
+ };
}
#[test]
@@ -190,11 +204,9 @@ mod tests {
#[test]
fn test_defined_in_function() {
- global_block!(
- static MY_BLOCK = || -> i32 {
- 42
- }
- );
+ global_block!(static MY_BLOCK = || -> i32 {
+ 42
+ });
assert_eq!(unsafe { MY_BLOCK.call(()) }, 42);
}
}
diff --git a/block2/src/lib.rs b/block2/src/lib.rs
index 3d253fb1d..4b083ebf4 100644
--- a/block2/src/lib.rs
+++ b/block2/src/lib.rs
@@ -1,62 +1,78 @@
-/*!
-A Rust interface for Objective-C blocks.
-
-For more information on the specifics of the block implementation, see
-Clang's documentation:
-
-# Invoking blocks
-
-The `Block` struct is used for invoking blocks from Objective-C. For example,
-consider this Objective-C function:
-
-``` objc
-int32_t sum(int32_t (^block)(int32_t, int32_t)) {
- return block(5, 8);
-}
-```
-
-We could write it in Rust as the following:
-
-```
-# use block2::Block;
-unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 {
- block.call((5, 8))
-}
-```
-
-Note the extra parentheses in the `call` method, since the arguments must be
-passed as a tuple.
-
-# Creating blocks
-
-Creating a block to pass to Objective-C can be done with the `ConcreteBlock`
-struct. For example, to create a block that adds two `i32`s, we could write:
-
-```
-# use block2::ConcreteBlock;
-let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
-let block = block.copy();
-assert_eq!(unsafe { block.call((5, 8)) }, 13);
-```
-
-It is important to copy your block to the heap (with the `copy` method) before
-passing it to Objective-C; this is because our `ConcreteBlock` is only meant
-to be copied once, and we can enforce this in Rust, but if Objective-C code
-were to copy it twice we could have a double free.
-
-As an optimization if your block doesn't capture any variables, you can use
-the [`global_block!`] macro to create a static block:
-
-```
-use block2::global_block;
-global_block! {
- static MY_BLOCK = || -> f32 {
- 10.0
- }
-};
-assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0);
-```
-*/
+//! # Apple's C language extension of blocks
+//!
+//! C Blocks are effectively the C-equivalent of Rust's closures, in that they
+//! have the ability to capture their environments.
+//!
+//! This crate provides capabilities to create and invoke these blocks, in an
+//! ergonomic "Rust-centric" fashion.
+//!
+//! For more information on the specifics of the block implementation, see the
+//! [C language specification][lang] and the [ABI specification][ABI].
+//!
+//! (Note that while this library can be used separately from Objective-C,
+//! they're most commonly used together).
+//!
+//! ## Invoking blocks
+//!
+//! The [`Block`] struct is used for invoking blocks from Objective-C. For
+//! example, consider this Objective-C function that takes a block as a
+//! parameter, executes the block with some arguments, and returns the result:
+//!
+//! ```objc
+//! #include
+//! #include
+//! int32_t run_block(int32_t (^block)(int32_t, int32_t)) {
+//! return block(5, 8);
+//! }
+//! ```
+//!
+//! We could write the equivalent function in Rust like this:
+//!
+//! ```
+//! use block2::Block;
+//! unsafe fn run_block(block: &Block<(i32, i32), i32>) -> i32 {
+//! block.call((5, 8))
+//! }
+//! ```
+//!
+//! Note the extra parentheses in the `call` method, since the arguments must
+//! be passed as a tuple.
+//!
+//! ## Creating blocks
+//!
+//! Creating a block to pass to Objective-C can be done with the
+//! [`ConcreteBlock`] struct. For example, to create a block that adds two
+//! integers, we could write:
+//!
+//! ```
+//! use block2::ConcreteBlock;
+//! let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
+//! let block = block.copy();
+//! assert_eq!(unsafe { block.call((5, 8)) }, 13);
+//! ```
+//!
+//! It is important to copy your block to the heap (with the [`copy`] method)
+//! before passing it to Objective-C; this is because our [`ConcreteBlock`] is
+//! only meant to be copied once, and we can enforce this in Rust, but if
+//! Objective-C code were to copy it twice we could have a double free.
+//!
+//! [`copy`]: ConcreteBlock::copy
+//!
+//! As an optimization if your block doesn't capture any variables, you can
+//! use the [`global_block!`] macro to create a static block:
+//!
+//! ```
+//! use block2::global_block;
+//! global_block! {
+//! static MY_BLOCK = || -> f32 {
+//! 10.0
+//! };
+//! }
+//! assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0);
+//! ```
+//!
+//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html
+//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html
#![no_std]
#![warn(elided_lifetimes_in_paths)]
@@ -69,6 +85,10 @@ assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0);
extern crate std;
+#[cfg(doctest)]
+#[doc = include_str!("../README.md")]
+extern "C" {}
+
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
diff --git a/objc-sys/README.md b/objc-sys/README.md
index cbb73dbd1..dbe756948 100644
--- a/objc-sys/README.md
+++ b/objc-sys/README.md
@@ -3,11 +3,14 @@
[](https://crates.io/crates/objc-sys)
[](../LICENSE.txt)
[](https://docs.rs/objc-sys/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Raw Rust bindings to the Objective-C runtime and ABI.
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
+
+
## Runtime Support
Objective-C has a runtime, different implementations of said runtime exist,
@@ -15,6 +18,7 @@ and they act in slightly different ways. By default, Apple platforms link to
Apple's runtime, but if you're using another runtime you must tell it to this
library using feature flags.
+
### Apple's [`objc4`](https://opensource.apple.com/source/objc4/)
- Feature flag: `apple`.
@@ -145,5 +149,5 @@ Some items (in particular the `objc_msgSend_X` family) have `cfg`s that prevent
their usage on different platforms; these are **semver-stable** in the sense
that they will only get less restrictive, never more.
-1 That said, most of this is created with the help of `bindgen`'s
-commandline interface, so huge thanks to them!
+1: That said, most of this is created with the help of `bindgen`'s
+commandline interface, so huge thanks to them!
diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs
index 5e929f97c..6e3363235 100644
--- a/objc-sys/src/lib.rs
+++ b/objc-sys/src/lib.rs
@@ -7,6 +7,9 @@
//! particular `runtime.h`.
//! - GNUStep's `libobjc2` [source code][libobjc2], in particular `runtime.h`.
//!
+//! See also the [`README.md`](https://crates.io/crates/objc-sys) for more
+//! background information, and for how to configure the desired runtime.
+//!
//! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
//! [libobjc2]: https://github.com/gnustep/libobjc2/tree/v2.1/objc
//! [objc4]: https://opensource.apple.com/source/objc4/objc4-818.2/runtime/
@@ -26,6 +29,10 @@
// See https://github.com/japaric/cty/issues/14.
extern crate std;
+#[cfg(doctest)]
+#[doc = include_str!("../README.md")]
+extern "C" {}
+
use core::cell::UnsafeCell;
use core::marker::{PhantomData, PhantomPinned};
@@ -64,3 +71,18 @@ pub use various::*;
///
/// TODO: Replace this with `extern type` to also mark it as `!Sized`.
type OpaqueData = PhantomData<(UnsafeCell<()>, *const UnsafeCell<()>, PhantomPinned)>;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::ffi::CStr;
+
+ #[test]
+ fn smoke() {
+ // Verify that this library links and works fine by itself
+ let name = CStr::from_bytes_with_nul(b"abc:def:\0").unwrap();
+ let sel = unsafe { sel_registerName(name.as_ptr()) };
+ let rtn = unsafe { CStr::from_ptr(sel_getName(sel)) };
+ assert_eq!(name, rtn);
+ }
+}
diff --git a/objc2-encode/CHANGELOG.md b/objc2-encode/CHANGELOG.md
index 3588b2036..6f1183425 100644
--- a/objc2-encode/CHANGELOG.md
+++ b/objc2-encode/CHANGELOG.md
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
+### Added
+* Implement `Hash` for `Encoding`.
+
+### Changed
+* Improved documentation.
+
## 2.0.0-beta.1 - 2021-12-22
diff --git a/objc2-encode/README.md b/objc2-encode/README.md
index e0fd44aae..33e4ba611 100644
--- a/objc2-encode/README.md
+++ b/objc2-encode/README.md
@@ -3,70 +3,17 @@
[](https://crates.io/crates/objc2-encode)
[](../LICENSE.txt)
[](https://docs.rs/objc2-encode/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Objective-C type-encoding in Rust.
-The Objective-C directive `@encode` encodes types as strings for usage in
-various places in the runtime.
+This crates provides the equivalent of the Objective-C `@encode` directive,
+and functions for comparing these encodings.
-This crate provides the `Encoding` type to describe and compare these
-type-encodings without memory allocation.
+Additionally, it provides traits for annotating types that has an Objective-C
+encoding.
-Additionally it provides traits for annotating types that has a corresponding
-Objective-C encoding, respectively `Encode` for structs and `RefEncode` for
-references (and `EncodeArguments` for function arguments).
+See [the docs](https://docs.rs/objc2-encode/) for a more thorough overview.
-These types are exported under the `objc2` crate as well, so usually you would
-just use that.
-
-
-## Examples
-
-Implementing `Encode` and `RefEncode`:
-
-```rust
-use objc2_encode::{Encode, Encoding, RefEncode};
-
-#[repr(C)]
-struct MyObject {
- a: f32,
- b: i16,
-}
-
-unsafe impl Encode for MyObject {
- const ENCODING: Encoding<'static> = Encoding::Struct(
- "MyObject",
- &[f32::ENCODING, i16::ENCODING],
- );
-}
-
-assert!(MyObject::ENCODING.equivalent_to_str("{MyObject=fs}"));
-
-unsafe impl RefEncode for MyObject {
- const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
-}
-
-assert!(MyObject::ENCODING_REF.equivalent_to_str("^{MyObject=fs}"));
-```
-
-An `Encoding` can be compared with an encoding string from the Objective-C
-runtime:
-
-```rust
-use objc2_encode::Encode;
-assert!(i32::ENCODING.equivalent_to_str("i"));
-```
-
-`Encoding` implements `Display` as its string representation. This can be
-generated conveniently through the `to_string` method:
-
-```rust
-use objc2_encode::Encode;
-assert_eq!(i32::ENCODING.to_string(), "i");
-```
-
-See the [`examples`] folder for more complex usage.
-
-[`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
diff --git a/objc2-encode/src/encoding.rs b/objc2-encode/src/encoding.rs
index f52791018..85491f899 100644
--- a/objc2-encode/src/encoding.rs
+++ b/objc2-encode/src/encoding.rs
@@ -6,16 +6,33 @@ use crate::parse;
///
/// Can be retrieved in Objective-C for a type `T` using the `@encode(T)`
/// directive.
-/// ```objective-c , ignore
+/// ```objc
/// NSLog(@"Encoding of NSException: %s", @encode(NSException));
/// ```
///
-/// For more information, see [Apple's documentation][ocrtTypeEncodings] and
-/// [`clang`'s source code for generating `@encode`][clang-src].
+/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`]
+/// into its string representation, that the the `@encode` directive would
+/// return. This can be used conveniently through the `to_string` method:
+///
+/// ```
+/// use objc2_encode::Encoding;
+/// assert_eq!(Encoding::Int.to_string(), "i");
+/// ```
+///
+/// For more information on the string value of an encoding, see [Apple's
+/// documentation][ocrtTypeEncodings].
///
/// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
-/// [clang-src]: https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+///
+/// # Examples
+///
+/// Comparing an encoding to a string from the Objective-C runtime:
+///
+/// ```
+/// use objc2_encode::Encoding;
+/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]"));
+/// ```
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive] // Maybe we're missing some encodings?
pub enum Encoding<'a> {
/// A C `char`. Corresponds to the `c` code.
diff --git a/objc2-encode/src/lib.rs b/objc2-encode/src/lib.rs
index 80e284cdc..6b256d162 100644
--- a/objc2-encode/src/lib.rs
+++ b/objc2-encode/src/lib.rs
@@ -2,10 +2,68 @@
//!
//! This is re-exported into the top level of `objc2`.
//!
+//! The Objective-C directive `@encode` encodes types as strings, and this is
+//! used in various places in the runtime.
+//!
+//! This crate provides the [`Encoding`] type to efficiently describe and
+//! compare these type-encodings.
+//!
+//! Additionally it provides traits for annotating types that has an
+//! Objective-C encoding: Specifically [`Encode`] for structs, [`RefEncode`]
+//! for references and [`EncodeArguments`] for function arguments.
+//!
+//! These types are exported under the [`objc2`] crate as well, so usually you
+//! would just use them from there.
+//!
+//! ## Example
+//!
+//! Implementing [`Encode`] and [`RefEncode`] for a custom type:
+//!
+//! ```
+//! use objc2_encode::{Encode, Encoding, RefEncode};
+//! // or from objc2:
+//! // use objc2::{Encode, Encoding, RefEncode};
+//!
+//! #[repr(C)]
+//! struct MyStruct {
+//! a: f32, // float
+//! b: i16, // int16_t
+//! }
+//!
+//! unsafe impl Encode for MyStruct {
+//! const ENCODING: Encoding<'static> = Encoding::Struct(
+//! "MyStruct", // Must use the same name as defined in C header files
+//! &[
+//! f32::ENCODING, // Same as Encoding::Float
+//! i16::ENCODING, // Same as Encoding::Short
+//! ],
+//! );
+//! }
+//!
+//! // @encode(MyStruct) -> "{MyStruct=fs}"
+//! assert!(MyStruct::ENCODING.equivalent_to_str("{MyStruct=fs}"));
+//!
+//! unsafe impl RefEncode for MyStruct {
+//! // Note that if `MyStruct` is an Objective-C instance, this should
+//! // be `Encoding::Object`.
+//! const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
+//! }
+//!
+//! // @encode(MyStruct*) -> "^{MyStruct=fs}"
+//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}"));
+//! ```
+//!
+//! See the [`examples`] folder for more complex usage.
+//!
+//! [`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples
+//!
//! Further resources:
-//! -
-//! -
-//! -
+//! - [Objective-C, Encoding and You](https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de).
+//! - [Apple's documentation on Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html).
+//! - [How are the digits in ObjC method type encoding calculated?](https://stackoverflow.com/a/11527925)
+//! - [`clang`'s source code for generating `@encode`](https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850).
+//!
+//! [`objc2`]: https://crates.io/crates/objc2
#![no_std]
#![warn(elided_lifetimes_in_paths)]
diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md
index ceae11fe2..3763a7e62 100644
--- a/objc2-foundation/CHANGELOG.md
+++ b/objc2-foundation/CHANGELOG.md
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
+### Added
+* Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`.
+* Implement `fmt::Display` for `NSValue`.
+* Implement `DefaultId` for relevant objects.
+* Implement `AsRef` and `Index` for `NSData` and `NSMutableData`.
+* Implement `AsMut` and `IndexMut` for `NSMutableData`.
+
+### Changed
+* **BREAKING**: Renamed `INSFastEnumeration::enumerator` to
+ `INSFastEnumeration::iter_fast`.
+
+### Removed
+* **BREAKING**: Removed `Deref` and `DerefMut` from `NSData` and
+ `NSMutableData`, since these invoke a non-trivial amount of code, and could
+ easily lead to hard-to-diagnose performance issues.
+
## 0.2.0-alpha.3 - 2021-12-22
diff --git a/objc2-foundation/Cargo.toml b/objc2-foundation/Cargo.toml
index 47d8a7e18..fa53fe4e8 100644
--- a/objc2-foundation/Cargo.toml
+++ b/objc2-foundation/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.2.0-alpha.3" # Remember to update html_root_url in lib.rs
authors = ["Steven Sheldon", "Mads Marquart "]
edition = "2018"
-description = "Bindings to the Objective-C Foundation framework"
+description = "Bindings to the Objective-C Cocoa Foundation framework"
keywords = ["objective-c", "macos", "ios", "cocoa", "uikit"]
categories = [
"api-bindings",
diff --git a/objc2-foundation/README.md b/objc2-foundation/README.md
index 3aa65c619..a7c1896a6 100644
--- a/objc2-foundation/README.md
+++ b/objc2-foundation/README.md
@@ -3,7 +3,14 @@
[](https://crates.io/crates/objc2-foundation)
[](../LICENSE.txt)
[](https://docs.rs/objc2-foundation/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
-Bindings to the Objective-C `Foundation` framework in Rust.
+Bindings to the Objective-C Cocoa `Foundation` framework in Rust.
+
+This library is very much in progress, consider using the more battle-tested
+[`cocoa-foundation`] crate in the meantime.
+
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
+
+[`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation
diff --git a/objc2-foundation/examples/class_with_lifetime.rs b/objc2-foundation/examples/class_with_lifetime.rs
index 962f90461..4c78d038c 100644
--- a/objc2-foundation/examples/class_with_lifetime.rs
+++ b/objc2-foundation/examples/class_with_lifetime.rs
@@ -35,14 +35,14 @@ impl<'a> MyObject<'a> {
fn get(&self) -> Option<&'a u8> {
unsafe {
let obj = &*(self as *const _ as *const Object);
- *obj.get_ivar("_number_ptr")
+ *obj.ivar("_number_ptr")
}
}
fn write(&mut self, number: u8) {
let ptr: &mut Option<&'a mut u8> = unsafe {
let obj = &mut *(self as *mut _ as *mut Object);
- obj.get_mut_ivar("_number_ptr")
+ obj.ivar_mut("_number_ptr")
};
if let Some(ptr) = ptr {
**ptr = number;
diff --git a/objc2-foundation/examples/custom_class.rs b/objc2-foundation/examples/custom_class.rs
index 2c9d1f28a..5acd1b616 100644
--- a/objc2-foundation/examples/custom_class.rs
+++ b/objc2-foundation/examples/custom_class.rs
@@ -31,7 +31,7 @@ impl MYObject {
fn number(&self) -> u32 {
unsafe {
let obj = &*(self as *const _ as *const Object);
- *obj.get_ivar("_number")
+ *obj.ivar("_number")
}
}
@@ -62,7 +62,7 @@ unsafe impl INSObject for MYObject {
}
extern "C" fn my_object_get_number(this: &Object, _cmd: Sel) -> u32 {
- unsafe { *this.get_ivar("_number") }
+ unsafe { *this.ivar("_number") }
}
unsafe {
diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs
index 097fe8c6d..5ed39b4fb 100644
--- a/objc2-foundation/src/array.rs
+++ b/objc2-foundation/src/array.rs
@@ -2,11 +2,11 @@ use alloc::vec::Vec;
use core::cmp::Ordering;
use core::ffi::c_void;
use core::marker::PhantomData;
-use core::ops::{Index, Range};
+use core::ops::{Index, IndexMut, Range};
use core::ptr::NonNull;
use objc2::msg_send;
-use objc2::rc::{Id, Owned, Ownership, Shared, SliceId};
+use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
use objc2::runtime::Object;
use super::{
@@ -229,6 +229,15 @@ impl Index for NSArray {
}
}
+impl DefaultId for NSArray {
+ type Ownership = O;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
pub unsafe trait INSMutableArray: INSArray {
#[doc(alias = "addObject:")]
fn push(&mut self, obj: Id) {
@@ -378,6 +387,21 @@ impl Index for NSMutableArray {
}
}
+impl IndexMut for NSMutableArray {
+ fn index_mut(&mut self, index: usize) -> &mut T {
+ self.get_mut(index).unwrap()
+ }
+}
+
+impl DefaultId for NSMutableArray {
+ type Ownership = Owned;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::format;
diff --git a/objc2-foundation/src/comparison_result.rs b/objc2-foundation/src/comparison_result.rs
index 1c6f1ec94..52342c5cf 100644
--- a/objc2-foundation/src/comparison_result.rs
+++ b/objc2-foundation/src/comparison_result.rs
@@ -3,7 +3,7 @@ use core::cmp::Ordering;
use objc2::{Encode, Encoding, RefEncode};
#[repr(isize)] // NSInteger
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NSComparisonResult {
Ascending = -1,
Same = 0,
diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs
index aed26318b..1eb774087 100644
--- a/objc2-foundation/src/data.rs
+++ b/objc2-foundation/src/data.rs
@@ -1,12 +1,12 @@
#[cfg(feature = "block")]
use alloc::vec::Vec;
-use core::ops::{Deref, DerefMut, Range};
-use core::slice;
+use core::ops::{Index, IndexMut, Range};
+use core::slice::{self, SliceIndex};
use core::{ffi::c_void, ptr::NonNull};
use super::{INSCopying, INSMutableCopying, INSObject, NSRange};
use objc2::msg_send;
-use objc2::rc::{Id, Owned, Ownership, Shared};
+use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared};
pub unsafe trait INSData: INSObject {
type Ownership: Ownership;
@@ -111,14 +111,30 @@ unsafe impl INSMutableCopying for NSData {
type Output = NSMutableData;
}
-impl Deref for NSData {
- type Target = [u8];
-
- fn deref(&self) -> &[u8] {
+impl AsRef<[u8]> for NSData {
+ fn as_ref(&self) -> &[u8] {
self.bytes()
}
}
+impl> Index for NSData {
+ type Output = I::Output;
+
+ #[inline]
+ fn index(&self, index: I) -> &Self::Output {
+ Index::index(self.bytes(), index)
+ }
+}
+
+impl DefaultId for NSData {
+ type Ownership = Shared;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
pub unsafe trait INSMutableData: INSData {
fn bytes_mut(&mut self) -> &mut [u8] {
let ptr: *mut c_void = unsafe { msg_send![self, mutableBytes] };
@@ -180,20 +196,43 @@ unsafe impl INSMutableCopying for NSMutableData {
type Output = NSMutableData;
}
-impl Deref for NSMutableData {
- type Target = [u8];
-
- fn deref(&self) -> &[u8] {
+impl AsRef<[u8]> for NSMutableData {
+ fn as_ref(&self) -> &[u8] {
self.bytes()
}
}
-impl DerefMut for NSMutableData {
- fn deref_mut(&mut self) -> &mut [u8] {
+impl AsMut<[u8]> for NSMutableData {
+ fn as_mut(&mut self) -> &mut [u8] {
self.bytes_mut()
}
}
+impl> Index for NSMutableData {
+ type Output = I::Output;
+
+ #[inline]
+ fn index(&self, index: I) -> &Self::Output {
+ Index::index(self.bytes(), index)
+ }
+}
+
+impl> IndexMut for NSMutableData {
+ #[inline]
+ fn index_mut(&mut self, index: I) -> &mut Self::Output {
+ IndexMut::index_mut(self.bytes_mut(), index)
+ }
+}
+
+impl DefaultId for NSMutableData {
+ type Ownership = Owned;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::{INSData, INSMutableData, NSData, NSMutableData};
diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs
index ab60e6da6..62414191d 100644
--- a/objc2-foundation/src/dictionary.rs
+++ b/objc2-foundation/src/dictionary.rs
@@ -5,7 +5,7 @@ use core::ops::Index;
use core::ptr::{self, NonNull};
use objc2::msg_send;
-use objc2::rc::{Id, Owned, Ownership, Shared, SliceId};
+use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
use super::{INSCopying, INSFastEnumeration, INSObject, NSArray, NSEnumerator};
@@ -153,6 +153,15 @@ impl NSDictionary {
unsafe_def_fn!(pub fn new -> Shared);
}
+impl DefaultId for NSDictionary {
+ type Ownership = Shared;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
unsafe impl INSDictionary for NSDictionary {
type Key = K;
type Value = V;
@@ -166,7 +175,7 @@ unsafe impl INSFastEnumeration for NSDictionary Index<&'a K> for NSDictionary {
type Output = V;
- fn index(&self, index: &K) -> &V {
+ fn index<'s>(&'s self, index: &'a K) -> &'s V {
self.get(index).unwrap()
}
}
diff --git a/objc2-foundation/src/enumerator.rs b/objc2-foundation/src/enumerator.rs
index 32bcba0f7..f54b3fb9d 100644
--- a/objc2-foundation/src/enumerator.rs
+++ b/objc2-foundation/src/enumerator.rs
@@ -44,7 +44,7 @@ impl<'a, T: INSObject> Iterator for NSEnumerator<'a, T> {
pub unsafe trait INSFastEnumeration: INSObject {
type Item: INSObject;
- fn enumerator(&self) -> NSFastEnumerator<'_, Self> {
+ fn iter_fast(&self) -> NSFastEnumerator<'_, Self> {
NSFastEnumerator::new(self)
}
}
@@ -212,10 +212,10 @@ mod tests {
let vec = (0u32..4).map(NSValue::new).collect();
let array = NSArray::from_vec(vec);
- let enumerator = array.enumerator();
+ let enumerator = array.iter_fast();
assert_eq!(enumerator.count(), 4);
- let enumerator = array.enumerator();
+ let enumerator = array.iter_fast();
assert!(enumerator.enumerate().all(|(i, obj)| obj.get() == i as u32));
}
}
diff --git a/objc2-foundation/src/lib.rs b/objc2-foundation/src/lib.rs
index c18f20c6e..56933c887 100644
--- a/objc2-foundation/src/lib.rs
+++ b/objc2-foundation/src/lib.rs
@@ -1,3 +1,10 @@
+//! # Bindings to the Objective-C Cocoa `Foundation` framework
+//!
+//! This library is very much in progress, consider using the more
+//! battle-tested [`cocoa-foundation`] crate in the meantime.
+//!
+//! [`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation
+
#![no_std]
#![warn(elided_lifetimes_in_paths)]
#![deny(non_ascii_idents)]
diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs
index 049fb5c8c..290c4d761 100644
--- a/objc2-foundation/src/object.rs
+++ b/objc2-foundation/src/object.rs
@@ -2,7 +2,7 @@ use core::marker::PhantomData;
use core::ptr::NonNull;
use objc2::msg_send;
-use objc2::rc::{Id, Owned, Shared};
+use objc2::rc::{DefaultId, Id, Owned, Shared};
use objc2::runtime::{Bool, Class, Object};
use objc2::Message;
@@ -55,6 +55,15 @@ impl NSObject {
unsafe_def_fn!(pub fn new -> Owned);
}
+impl DefaultId for NSObject {
+ type Ownership = Owned;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::{INSObject, NSObject};
diff --git a/objc2-foundation/src/string.rs b/objc2-foundation/src/string.rs
index c3570d07e..64635b77c 100644
--- a/objc2-foundation/src/string.rs
+++ b/objc2-foundation/src/string.rs
@@ -8,6 +8,7 @@ use std::os::raw::c_char;
use alloc::borrow::ToOwned;
use objc2::ffi;
use objc2::msg_send;
+use objc2::rc::DefaultId;
use objc2::rc::{autoreleasepool, AutoreleasePool};
use objc2::rc::{Id, Shared};
@@ -111,6 +112,15 @@ impl NSString {
unsafe_def_fn!(pub fn new -> Shared);
}
+impl DefaultId for NSString {
+ type Ownership = Shared;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new()
+ }
+}
+
unsafe impl INSString for NSString {}
unsafe impl INSCopying for NSString {
diff --git a/objc2-foundation/src/value.rs b/objc2-foundation/src/value.rs
index e886a1d5e..a537fa5c3 100644
--- a/objc2-foundation/src/value.rs
+++ b/objc2-foundation/src/value.rs
@@ -1,14 +1,15 @@
use alloc::string::ToString;
+use core::cmp::Ordering;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
-use core::str;
+use core::{fmt, str};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use objc2::msg_send;
-use objc2::rc::{Id, Shared};
+use objc2::rc::{DefaultId, Id, Shared};
use objc2::Encode;
use super::{INSCopying, INSObject};
@@ -91,6 +92,33 @@ unsafe impl INSCopying for NSValue {
type Output = NSValue;
}
+impl Ord for NSValue {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.get().cmp(&other.get())
+ }
+}
+
+impl PartialOrd for NSValue {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ self.get().partial_cmp(&other.get())
+ }
+}
+
+impl DefaultId for NSValue {
+ type Ownership = Shared;
+
+ #[inline]
+ fn default_id() -> Id {
+ Self::new(Default::default())
+ }
+}
+
+impl fmt::Display for NSValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
/// ```compile_fail
/// use objc2_foundation::NSValue;
/// fn needs_eq() {}
diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md
index 8bcc68dcb..3348f6cd8 100644
--- a/objc2/CHANGELOG.md
+++ b/objc2/CHANGELOG.md
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
+### Added
+* Implement `Hash` for `Sel`, `Ivar`, `Class`, `Method` and `MessageError`.
+* Implement `PartialEq` and `Eq` for `Ivar`, `Method` and `MessageError`.
+* Implement `fmt::Pointer` for `Sel` and `rc::AutoreleasePool`.
+* Implement `fmt::Debug` for `ClassDecl`, `ProtocolDecl` and `rc::AutoreleasePool`.
+
+### Changed
+* **BREAKING**: Renamed:
+ - `Object::get_ivar` -> `Object::ivar`
+ - `Object::get_mut_ivar` -> `Object::ivar_mut`
+* Vastly improved documentation.
+
## 0.3.0-alpha.5 - 2021-12-22
diff --git a/objc2/Cargo.toml b/objc2/Cargo.toml
index e551af72d..e2079e6c3 100644
--- a/objc2/Cargo.toml
+++ b/objc2/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.3.0-alpha.5" # Remember to update html_root_url in lib.rs
authors = ["Steven Sheldon", "Mads Marquart "]
edition = "2018"
-description = "Objective-C runtime bindings and interface."
+description = "Objective-C interface and runtime bindings"
keywords = ["objective-c", "macos", "ios", "objc_msgSend"]
categories = [
"api-bindings",
@@ -18,6 +18,8 @@ license = "MIT"
build = "build.rs"
+# NOTE: 'unstable' features are _not_ considered part of the SemVer contract,
+# and may be removed in a minor release.
[features]
# Enables `objc2::exception::throw` and `objc2::exception::catch`
exception = ["cc"]
diff --git a/objc2/README.md b/objc2/README.md
index cd2bda13e..cf818d105 100644
--- a/objc2/README.md
+++ b/objc2/README.md
@@ -3,116 +3,34 @@
[](https://crates.io/crates/objc2)
[](../LICENSE.txt)
[](https://docs.rs/objc2/)
-[](https://github.com/madsmtm/objc2/actions/workflows/apple.yml)
-[](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
-Objective-C runtime bindings and interface for Rust.
+Objective-C interface and runtime bindings for Rust.
-## Messaging objects
+Most of the core libraries and frameworks that are in use on Apple systems are
+written in Objective-C; this crate enables you to interract with those.
-Objective-C objects can be messaged using the `msg_send!` macro:
+## Example
-```rust , no_run
+```rust
+use std::ptr::NonNull;
use objc2::{class, msg_send};
-use objc2::runtime::{Bool, Object};
+use objc2::rc::{Id, Owned};
+use objc2::runtime::{Class, Object};
let cls = class!(NSObject);
-unsafe {
- let obj: *mut Object = msg_send![cls, new];
- let hash: usize = msg_send![obj, hash];
- let is_kind: Bool = msg_send![obj, isKindOfClass: cls];
- // Even void methods must have their return type annotated
- let _: () = msg_send![obj, release];
-}
-```
-
-## Reference counting
-
-The utilities of the `rc` module provide ARC-like semantics for working with
-Objective-C's reference counted objects in Rust.
-
-An `Id` retains an object and releases the object when dropped.
-A `WeakId` will not retain the object, but can be upgraded to an `Id` and
-safely fails if the object has been deallocated.
-
-```rust , no_run
-use objc2::{class, msg_send};
-use objc2::rc::{autoreleasepool, Id, Shared, WeakId};
-use objc2::runtime::Object;
-
-// Id will release the object when dropped
-let obj: Id