type_census/
lib.rs

1//! Track the number of extant instances of your types.
2//!
3//! ## Example
4//! ```
5//! // 1. import these two items:
6//! use type_census::{Instance, Tabulate};
7//!
8//! // 2. Derive `Tabulate`
9//! #[derive(Clone, Tabulate)]
10//! pub struct Foo<T> {
11//!     v: T,
12//!     // 3. add a field of type `Instance<Self>`
13//!     _instance: Instance<Self>,
14//! }
15//!
16//! impl<T> Foo<T> {
17//!     pub fn new(v: T) -> Self
18//!     where
19//!         // 4. add a `Self: Tabulate` bound to constructors
20//!         Self: Tabulate,
21//!     {
22//!         Self {
23//!             v,
24//!             // 5. and initialize your `Instance` field like so:
25//!             _instance: Instance::new(),
26//!         }
27//!     }
28//!
29//!     pub fn v(self) -> T {
30//!         self.v
31//!     }
32//! }
33//!
34//! fn main() {
35//!     // you can now query the number of extant instances of `Foo`!
36//!     assert_eq!(Foo::<i8>::instances(), 0);
37//!     assert_eq!(Foo::<u8>::instances(), 0);
38//!
39//!     // the same counter is shared for all generic instantiations
40//!     let mut bar: Vec<Foo<i8>> = vec![Foo::new(0i8); 10];
41//!
42//!     assert_eq!(Foo::<i8>::instances(), 10);
43//!     assert_eq!(Foo::<u8>::instances(), 10);
44//!
45//!     let _baz: Vec<Foo<u8>> = vec![Foo::new(0u8); 5];
46//!
47//!     assert_eq!(Foo::<i8>::instances(), 15);
48//!     assert_eq!(Foo::<u8>::instances(), 15);
49//!
50//!     let _ = bar.drain(0..5);
51//!
52//!     assert_eq!(Foo::<i8>::instances(), 10);
53//!     assert_eq!(Foo::<u8>::instances(), 10);
54//! }
55//! ```
56#![deny(missing_docs)]
57
58use num_traits::identities::one;
59use std::marker::PhantomData;
60
61pub mod counter;
62
63use counter::Counter;
64
65/// Automatically derive the implementation of [`Tabulate`].
66///
67/// By default, this uses [`counter::RelaxedCounter`] to count the instances.
68/// You can use a different counter type like so:
69/// ```
70/// // 1. import these two items:
71/// use type_census::{Instance, Tabulate};
72/// 
73/// // 2. Derive `Tabulate`
74/// // This will count instances with a `DistributedCounter` with 32 buckets.
75/// #[derive(Clone, Tabulate)]
76/// #[Tabulate(Counter = "type_census::counter::DistributedCounter<32>")]
77/// pub struct Foo<T> {
78///     v: T,
79///     // 3. add a field of type `Instance<Self>`
80///     _instance: Instance<Self>,
81/// }
82/// ```
83pub use type_census_derive::Tabulate;
84
85/// A zero-sized guard that tracks the lifetime of an instance of `T`.
86///
87/// Constructing an `Instance<T>` increments the population count of `T`.
88/// Dropping an `Instance<T>` decrements the population count of `T`.
89#[repr(transparent)]
90pub struct Instance<T>
91where
92    T: Tabulate,
93{
94    _tabulated: PhantomData<T>,
95}
96
97impl<T> Instance<T>
98where
99    T: Tabulate,
100{
101    /// Constructs a new `Instance<T>`, representing the extant lifetime of
102    /// an instance of `T`.
103    #[inline(always)]
104    pub fn new() -> Self {
105        T::counter().add_assign(one());
106        Instance {
107            _tabulated: PhantomData,
108        }
109    }
110}
111
112impl<T> std::fmt::Debug for Instance<T>
113where
114    T: Tabulate,
115{
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        f.debug_struct(std::any::type_name::<Self>()).finish()
118    }
119}
120
121impl<T> Default for Instance<T>
122where
123    T: Tabulate,
124{
125    #[inline(always)]
126    fn default() -> Self {
127        Self::new()
128    }
129}
130
131impl<T> Clone for Instance<T>
132where
133    T: Tabulate,
134{
135    #[inline(always)]
136    fn clone(&self) -> Self {
137        Self::new()
138    }
139}
140
141impl<T> Drop for Instance<T>
142where
143    T: Tabulate,
144{
145    #[inline(always)]
146    fn drop(&mut self) {
147        T::counter().sub_assign(one());
148    }
149}
150
151impl<T> std::hash::Hash for Instance<T>
152where
153    T: Tabulate,
154{
155    #[inline(always)]
156    fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
157}
158
159impl<T> Ord for Instance<T>
160where
161    T: Tabulate,
162{
163    #[inline(always)]
164    fn cmp(&self, _: &Self) -> std::cmp::Ordering {
165        std::cmp::Ordering::Equal
166    }
167}
168
169impl<T> PartialOrd for Instance<T>
170where
171    T: Tabulate,
172{
173    #[inline(always)]
174    fn partial_cmp(&self, _: &Self) -> Option<std::cmp::Ordering> {
175        Some(std::cmp::Ordering::Equal)
176    }
177}
178
179impl<T> Eq for Instance<T> where T: Tabulate {}
180
181impl<T> PartialEq for Instance<T>
182where
183    T: Tabulate,
184{
185    #[inline(always)]
186    fn eq(&self, _: &Self) -> bool {
187        true
188    }
189}
190
191/// Track the population of `Self`.
192pub trait Tabulate: Sized {
193    /// The type of the counter used to track instances of `Self`.
194    type Counter: Counter;
195
196    /// Produces a reference to the counter tracking instances of `Self`.
197    fn counter() -> &'static Self::Counter;
198
199    /// Produces the number of extant instances of `T`.
200    fn instances() -> <Self::Counter as Counter>::Primitive {
201        Self::counter().fetch()
202    }
203}