competitive/tools/
associated_value.rs

1/// Trait for a modifiable value associated with a type.
2pub trait AssociatedValue {
3    /// Type of value.
4    type T: 'static + Clone;
5    unsafe fn __local_key() -> &'static std::thread::LocalKey<std::cell::Cell<Self::T>>;
6    #[inline]
7    fn get() -> Self::T {
8        Self::with(Clone::clone)
9    }
10    #[inline]
11    fn set(x: Self::T) {
12        unsafe { Self::__local_key().with(|cell| cell.set(x)) }
13    }
14    #[inline]
15    fn replace(x: Self::T) -> Self::T {
16        unsafe { Self::__local_key().with(|cell| cell.replace(x)) }
17    }
18    #[inline]
19    fn with<F, R>(f: F) -> R
20    where
21        F: FnOnce(&Self::T) -> R,
22    {
23        unsafe { Self::__local_key().with(|cell| f(&*cell.as_ptr())) }
24    }
25    #[inline]
26    fn modify<F, R>(f: F) -> R
27    where
28        F: FnOnce(&mut Self::T) -> R,
29    {
30        unsafe { Self::__local_key().with(|cell| f(&mut *cell.as_ptr())) }
31    }
32}
33
34/// Implement [`AssociatedValue`].
35///
36/// [`AssociatedValue`]: super::AssociatedValue
37///
38/// # Examples
39///
40/// ```
41/// use competitive::tools::AssociatedValue;
42/// enum X {}
43/// competitive::impl_assoc_value!(X, usize, 1);
44/// assert_eq!(X::get(), 1);
45/// X::set(10);
46/// assert_eq!(X::get(), 10);
47/// ```
48///
49/// init with `Default::default()`
50///
51/// ```
52/// use competitive::tools::AssociatedValue;
53/// enum X {}
54/// competitive::impl_assoc_value!(X, usize);
55/// assert_eq!(X::get(), Default::default());
56/// ```
57#[macro_export]
58macro_rules! impl_assoc_value {
59    ($name:ident, $t:ty) => {
60        $crate::impl_assoc_value!($name, $t, Default::default());
61    };
62    ($name:ident, $t:ty, $e:expr) => {
63        impl AssociatedValue for $name {
64            type T = $t;
65            #[inline]
66            unsafe fn __local_key() -> &'static ::std::thread::LocalKey<::std::cell::Cell<Self::T>> {
67                ::std::thread_local!(static __LOCAL_KEY: ::std::cell::Cell<$t> = ::std::cell::Cell::new($e));
68                &__LOCAL_KEY
69            }
70        }
71    };
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::impl_assoc_value;
78
79    #[test]
80    fn test_associated_value() {
81        enum X {}
82        impl_assoc_value!(X, usize);
83        X::set(10);
84        assert_eq!(X::get(), 10);
85        assert_eq!(X::with(|x| x + 1), 11);
86        X::modify(|x| *x += 1);
87        assert_eq!(X::get(), 11);
88    }
89}