1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
// Copyright Christian Daley 2021 // Copyright Frank Mori Hess 2007-2008. // Distributed under the Boost Software License, Version 1.0. // See http://www.boost.org/LICENSE_1_0.txt //! # signals2 //! //! `signals2` is a thread-safe signal/slot library based on the [boost::signals2](https://www.boost.org/doc/libs/1_76_0/doc/html/signals2.html) //! C++ library. [Signals](Signal) are objects that contain a list of callback functions ("slots") to be executed when the signal is //! "emitted". Signals and their corresponding slots can be managed through the use of [connections](Connection) //! and [shared connection blocks](SharedConnectionBlock). //! //! `signals2` contains no unsafe code and compiles on stable Rust 1.53. //! //! `signals2` is distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). //! //! ### Links //! * [Github](https://github.com/christiandaley/signals2/) #![deny(missing_docs)] use std::sync::{Arc, Weak, Mutex}; mod signal_core; use signal_core::{SignalCore}; /// Defines the combiner trait and several simple combiners that can be used. pub mod combiner; use combiner::{Combiner, DefaultCombiner}; /// Defines different `emit` traits for signals. pub mod emit; #[doc(inline)] pub use emit::{Emit0, Emit1, Emit2, Emit3, Emit4, Emit5, Emit6, Emit7, Emit8, Emit9, Emit10, Emit11, Emit12}; /// Defines different `connect` traits for signals. pub mod connect; #[doc(inline)] pub use connect::{SharedConnectionBlock, Connection, ScopedConnection, ConnectionInterface, Position, Group, Connect0, Connect1, Connect2, Connect3, Connect4, Connect5, Connect6, Connect7, Connect8, Connect9, Connect10, Connect11, Connect12}; /// A handle to a signal with a slot function signature of `Args -> R`. `C` defines the combiner used /// to generate a return value when `emit` is envoked. `G` defines the ordering of groups of slots. **Arguments given /// to the signal must implement `Clone`. If you need to emit a signal with an argument that doesn't implement clone, that /// argument should be wrapped in an `Arc<T>` (as an example) to make it cloneable.** /// # Examples /// ``` /// use signals2::*; /// /// let sig: Signal<()> = Signal::new(); /// sig.connect(|| println!("Hello, world!")); /// sig.emit(); // prints "Hello, world!" /// ``` /// The only required template parameter for a `Signal` is the type of the parameters that the slot functions will /// accept, represented as a tuple. If we want our signal to have slot functions that accept two `i32`s as parameters, the /// template parameter will be `(i32, i32)`. Slot functions may accept 0-12 parameters. /// ``` /// use signals2::*; /// /// let sig: Signal<(i32, i32)> = Signal::new(); /// sig.connect(|x, y| println!("x + y = {}", x + y)); /// sig.emit(2, 3); // prints "x + y = 5" /// ``` /// Special care must be taken when creating `Signals` with slots that accept only one parameter. The single parameter type /// must still be represented as a tuple in the type signature of the `Signal`. The Rust compiler does not recognize `(T)` as a tuple-type /// of arity one, rather it recognizes it as simply the type `T`. A comma must be used to force the Rust compiler to recognize it as a tuple, e.x. `(T,)` /// ``` /// use signals2::*; /// /// let sig: Signal<(i32,)> = Signal::new(); // Note that using Signal<(i32)> or Signal<i32> will not compile! /// sig.connect(|x| println!("x = {}", x)); /// sig.emit(7); // prints "x = 7" /// ``` /// Slot functions can have return values, and the return value of the entire `Signal` is determined by the [Combiner] type. /// The default combiner simply returns an `Option<R>` with a value of `Some(x)` where `x` is the value returned by /// the last slot executed, or `None` in the case that no slots were executed. /// ``` /// use signals2::*; /// /// let sig: Signal<(i32, i32), i32> = Signal::new(); /// assert_eq!(sig.emit(2, 3), None); // no slots have been added yet /// sig.connect(|x, y| x + y); /// assert_eq!(sig.emit(2, 3), Some(5)); /// ``` pub struct Signal<Args, R = (), C = DefaultCombiner, G = i32> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { core: Arc<Mutex<Arc<SignalCore<Args, R, C, G>>>> } impl<Args, R, C, G> Clone for Signal<Args, R, C, G> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { /// Clones the corresponding signal. Note that a `Signal` is really just a handle to /// an underlying collection of slots. Cloning a signal will result in two handles that /// "point" to the same slots. /// /// # Example /// ``` /// use signals2::*; /// /// let sig1: Signal<()> = Signal::new(); /// let sig2 = sig1.clone(); /// sig1.connect(|| println!("Hello, world!")); // connect to the first signal /// sig2.emit(); // prints "Hello, world!" because sig1 and sig2 share the same set of slots. /// ``` fn clone(&self) -> Self { Self { core: self.core.clone() } } } impl<Args, R, C, G> Signal<Args, R, C, G> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { /// Creates a new signal with a corresponding [Combiner]. pub fn new_with_combiner(combiner: C) -> Self { let core: SignalCore<Args, R, C, G> = SignalCore::new(combiner); Signal { core: Arc::new(Mutex::new(Arc::new(core))) } } /// Creates a [WeakSignal] that holds a weak reference to its underling slots. pub fn weak(&self) -> WeakSignal<Args, R, C, G> { WeakSignal { weak_core: Arc::downgrade(&self.core) } } /// Sets a new [Combiner] for the signal. pub fn set_combiner(&self, combiner: C) { let mut lock = self.core.lock().unwrap(); let mut new_core = (**lock).clone(); new_core.set_combiner(combiner); *lock = Arc::new(new_core); } /// Disconnects all slots from the signal. Will cause any existing [Connections](Connection) to enter a /// "disconnected" state. pub fn clear(&self) { self.core.lock().unwrap().disconnect_all(); let mut lock = self.core.lock().unwrap(); let mut new_core = (**lock).clone(); new_core.clear(); *lock = Arc::new(new_core); } /// Returns the number of connected slots for the signal. pub fn count(&self) -> usize { let lock = self.core.lock().unwrap(); lock.count() } } impl<Args, R, C, G> Signal<Args, R, C, G> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + Default + 'static, G: Ord + Send + Sync + 'static { /// Creates a new signal with a [Combiner] created by calling `C::default()`. pub fn new() -> Self { Self::new_with_combiner(C::default()) } } /// A weak reference to a signal's slots. Useful for allowing slots to maintain a persistant reference to their /// owning signal without causing a memory leak. /// # Example /// ``` /// use signals2::*; /// /// let sig: Signal<()> = Signal::new(); /// let weak_sig = sig.weak(); /// sig.connect(move || { /// // if we had captured a cloned sig here it would cause a memory leak. /// // Signals maintain strong references to their slot functions, so a slot function /// // should not maintain a strong reference to its own signal or else a memory leak /// // will occur. /// weak_sig.upgrade().unwrap().connect(|| println!("Hello, world!")); /// }); /// /// sig.emit(); // prints nothing /// sig.emit(); // prints "Hello, world!" once /// sig.emit(); // prints "Hello, world!" twice /// // etc... /// ``` pub struct WeakSignal<Args, R = (), C = DefaultCombiner, G = i32> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { weak_core: Weak<Mutex<Arc<SignalCore<Args, R, C, G>>>> } impl<Args, R, C, G> Clone for WeakSignal<Args, R, C, G> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { fn clone(&self) -> Self { Self { weak_core: self.weak_core.clone() } } } impl<Args, R, C, G> WeakSignal<Args, R, C, G> where Args: Clone + 'static, R: 'static, C: Combiner<R> + Send + Sync + 'static, G: Ord + Send + Sync + 'static { /// Returns `Some(sig)` where `sig` is the singal that the weak signal was /// created from. If the original signal (and all other clones of it) have been /// dropped, returns `None`. pub fn upgrade(&self) -> Option<Signal<Args, R, C, G>> { self.weak_core.upgrade().map(|core| Signal {core}) } }