From 5054c615b3f07f24cba7d1260410b7229f809593 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 21 Jan 2025 17:53:58 +0100 Subject: [PATCH] Implement comparison traits for Bool These are useful in many cases, and just an annoyance at this point that we don't have it. --- crates/header-translator/src/stmt.rs | 7 +-- crates/objc2/CHANGELOG.md | 2 + crates/objc2/src/runtime/bool.rs | 51 ++++++++++++--- .../objc2-avf-audio/translation-config.toml | 6 -- .../src/extended_gamepad_snapshot.rs | 63 ------------------- .../objc2-game-controller/src/lib.rs | 5 -- .../translation-config.toml | 3 - .../translation-config.toml | 3 - generated | 2 +- 9 files changed, 48 insertions(+), 94 deletions(-) delete mode 100644 framework-crates/objc2-game-controller/src/extended_gamepad_snapshot.rs diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 051978e2e..ab1f4c207 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -2399,12 +2399,7 @@ impl Stmt { if *is_union || fields.iter().any(|(_, _, field)| field.contains_union()) { writeln!(f, "#[derive(Clone, Copy)]")?; } else { - // HACK to make Bool in structs work. - if fields.iter().any(|(_, _, field)| field.is_objc_bool()) { - writeln!(f, "#[derive(Clone, Copy, Debug)]")?; - } else { - writeln!(f, "#[derive(Clone, Copy, Debug, PartialEq)]")?; - } + writeln!(f, "#[derive(Clone, Copy, Debug, PartialEq)]")?; } if *is_union { writeln!(f, "pub union {} {{", id.name)?; diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 890522a79..284d5816d 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -36,6 +36,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). insufficient). * Added `#[unsafe(method_family = ...)]` attribute in `extern_methods!` and `extern_protocol!`, to allow overriding the inferred method family. +* Added `PartialEq`, `Eq`, `Hash`, `PartialOrd` and `Ord` implementations for + `Bool`. ### Changed * **BREAKING**: Renamed `declare_class!` to `define_class!`, and changed the diff --git a/crates/objc2/src/runtime/bool.rs b/crates/objc2/src/runtime/bool.rs index 23152dda8..7f1072a29 100644 --- a/crates/objc2/src/runtime/bool.rs +++ b/crates/objc2/src/runtime/bool.rs @@ -1,5 +1,5 @@ #![allow(clippy::upper_case_acronyms)] -use core::fmt; +use core::{fmt, hash}; use crate::encode::{Encode, Encoding, RefEncode}; @@ -86,17 +86,13 @@ mod inner { /// around `bool` instead. /// /// Note that this is able to contain more states than `bool` on some -/// platforms, but these cases should not be relied on! +/// platforms, but these cases should not be relied on! The comparison traits +/// `PartialEq`, `PartialOrd` etc. will completely ignore these states. /// /// See also the [corresponding documentation entry][docs]. /// /// [docs]: https://developer.apple.com/documentation/objectivec/bool?language=objc #[repr(transparent)] -// We don't implement comparison traits because they could be implemented with -// two slightly different semantics: -// - `self.as_bool().cmp(other.as_bool())` -// - `self.value.cmp(other.value)` -// And it is not immediately clear for users which one was chosen. #[derive(Copy, Clone, Default)] pub struct Bool { value: inner::BOOL, @@ -181,6 +177,38 @@ impl fmt::Debug for Bool { } } +// Implement comparison traits by first converting to a boolean. + +impl PartialEq for Bool { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_bool() == other.as_bool() + } +} + +impl Eq for Bool {} + +impl hash::Hash for Bool { + #[inline] + fn hash(&self, state: &mut H) { + self.as_bool().hash(state); + } +} + +impl PartialOrd for Bool { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Bool { + #[inline] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.as_bool().cmp(&other.as_bool()) + } +} + trait Helper { const __ENCODING: Encoding; } @@ -278,6 +306,11 @@ mod tests { assert!(Bool::from(true).is_true()); assert!(Bool::from(false).is_false()); + + assert_eq!(Bool::new(true), Bool::new(true)); + assert_eq!(Bool::new(false), Bool::new(false)); + + assert!(Bool::new(false) < Bool::new(true)); } #[test] @@ -294,5 +327,9 @@ mod tests { assert!(b.is_true()); assert!(!b.is_false()); assert_eq!(b.as_raw(), 42); + + // PartialEq ignores extra data + assert_eq!(b, Bool::new(true)); + assert_ne!(b, Bool::new(false)); } } diff --git a/framework-crates/objc2-avf-audio/translation-config.toml b/framework-crates/objc2-avf-audio/translation-config.toml index c01529596..2a2b22fe7 100644 --- a/framework-crates/objc2-avf-audio/translation-config.toml +++ b/framework-crates/objc2-avf-audio/translation-config.toml @@ -29,12 +29,6 @@ class.AVAudioEngine.methods."renderOffline:toBuffer:error:".skipped = true # Non-null error return class.AVAudioSequencer.methods."dataWithSMPTEResolution:error:".skipped = true -# tries to derive with `PartialEq` which fails because of `Bool` fields -struct.AVAudioVoiceProcessingOtherAudioDuckingConfiguration.skipped = true -typedef.AVAudioVoiceProcessingOtherAudioDuckingConfiguration.skipped = true -class.AVAudioInputNode.methods.voiceProcessingOtherAudioDuckingConfiguration.skipped = true -class.AVAudioInputNode.methods."setVoiceProcessingOtherAudioDuckingConfiguration:".skipped = true - # Needs AppKit or UIKit class.AVAudioUnitComponent.methods.icon.skipped = true diff --git a/framework-crates/objc2-game-controller/src/extended_gamepad_snapshot.rs b/framework-crates/objc2-game-controller/src/extended_gamepad_snapshot.rs deleted file mode 100644 index 377ba9708..000000000 --- a/framework-crates/objc2-game-controller/src/extended_gamepad_snapshot.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(non_snake_case)] -use core::ffi::c_float; - -use objc2::encode::{Encode, Encoding, RefEncode}; -use objc2::runtime::Bool; - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -#[deprecated = "GCExtendedGamepadSnapshot has been deprecated, use [GCController controllerWithExtendedGamepad] instead"] -pub struct GCExtendedGamepadSnapshotData { - pub version: u16, - pub size: u16, - pub dpadX: c_float, - pub dpadY: c_float, - pub buttonA: c_float, - pub buttonB: c_float, - pub buttonX: c_float, - pub buttonY: c_float, - pub leftShoulder: c_float, - pub rightShoulder: c_float, - pub leftThumbstickX: c_float, - pub leftThumbstickY: c_float, - pub rightThumbstickX: c_float, - pub rightThumbstickY: c_float, - pub leftTrigger: c_float, - pub rightTrigger: c_float, - pub supportsClickableThumbsticks: Bool, - pub leftThumbstickButton: Bool, - pub rightThumbstickButton: Bool, -} - -#[allow(deprecated)] -unsafe impl Encode for GCExtendedGamepadSnapshotData { - const ENCODING: Encoding = Encoding::Struct( - "GCExtendedGamepadSnapshotData", - &[ - u16::ENCODING, - u16::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - c_float::ENCODING, - Bool::ENCODING, - Bool::ENCODING, - Bool::ENCODING, - ], - ); -} - -#[allow(deprecated)] -unsafe impl RefEncode for GCExtendedGamepadSnapshotData { - const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING); -} diff --git a/framework-crates/objc2-game-controller/src/lib.rs b/framework-crates/objc2-game-controller/src/lib.rs index 07ee1caeb..05bcdfea5 100644 --- a/framework-crates/objc2-game-controller/src/lib.rs +++ b/framework-crates/objc2-game-controller/src/lib.rs @@ -15,15 +15,10 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -#[cfg(feature = "GCExtendedGamepadSnapshot")] -mod extended_gamepad_snapshot; mod generated; #[cfg(feature = "GCInputNames")] mod input_names; -#[cfg(feature = "GCExtendedGamepadSnapshot")] -#[allow(deprecated)] -pub use self::extended_gamepad_snapshot::GCExtendedGamepadSnapshotData; #[allow(unused_imports, unreachable_pub)] pub use self::generated::*; #[cfg(feature = "GCInputNames")] diff --git a/framework-crates/objc2-game-controller/translation-config.toml b/framework-crates/objc2-game-controller/translation-config.toml index 3d2d5fefb..bca07fba2 100644 --- a/framework-crates/objc2-game-controller/translation-config.toml +++ b/framework-crates/objc2-game-controller/translation-config.toml @@ -50,9 +50,6 @@ static.GCInputRightPaddle.skipped = true # Uses GCInputButtonName fn.GCInputArcadeButtonName.skipped = true -# tries to derive with `PartialEq` which fails because of `Bool` fields; manually define for now -struct.GCExtendedGamepadSnapshotData.skipped = true - # Needs `CHHapticEngine` from the `CoreHaptics` framework class.GCDeviceHaptics.methods."createEngineWithLocality:".skipped = true diff --git a/framework-crates/objc2-metal-performance-shaders/translation-config.toml b/framework-crates/objc2-metal-performance-shaders/translation-config.toml index c7db8fd14..6978ebfe4 100644 --- a/framework-crates/objc2-metal-performance-shaders/translation-config.toml +++ b/framework-crates/objc2-metal-performance-shaders/translation-config.toml @@ -11,9 +11,6 @@ visionos = "1.0" # Defined in both MPSCore.MPSImage and MPSCore.MPSNDArray typedef.MPSImageBatch.skipped = true -# tries to derive with `PartialEq` which fails because of `Bool` fields -# struct.MPSImageHistogramInfo.skipped = true - # Has a union field, but we'd rather have it as just a struct struct._MPSPackedFloat3.skipped = true typedef.MPSPackedFloat3.skipped = true diff --git a/generated b/generated index 320b4bc62..84c7bca7b 160000 --- a/generated +++ b/generated @@ -1 +1 @@ -Subproject commit 320b4bc6266f90edbede5b449c2db4c93b4b5539 +Subproject commit 84c7bca7b5711c7a0d1130d5f854bc4b751d1a49