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}