basic_oop/
lib.rs

1#![feature(macro_metavar_expr_concat)]
2
3#![deny(warnings)]
4#![doc(test(attr(deny(warnings))))]
5#![doc(test(attr(allow(dead_code))))]
6#![doc(test(attr(allow(unused_variables))))]
7#![allow(clippy::needless_doctest_main)]
8
9//! The crate provides basic tools for writing object-oriented code in Rust.
10//! Very basic: no multiply inheritance, no generics, no interfaces
11//! (but this can be added in future in a limited form), no incapsulation.
12//! All classes in hierarchy should have distinct names even if they are located in different modules.
13//!
14//! # How to define a class
15//!
16//! First you need to _import_ the parent class. All classes should be derived from some parent
17//! class. The only class not having any parent is `Obj` defined in `basic_oop::obj` module. It contains
18//! no fields or methods. Lets import it:
19//!
20//! ```ignore
21//! import! { pub test_class:
22//!     use [obj basic_oop::obj];
23//! }
24//! ```
25//!
26//! Here `test_class` correspond to our new class name.
27//! (Although it is possible to use different names in the import section and in the class definition,
28//! doing so is not recommended to avoid confusion among users of the class.)
29//!
30//! The pub keyword means that our class will be public. In case of a private class, it should be omitted.
31//!
32//! All types that we plan to use in the method signatures of our class
33//! should also be imported as unique names. For example:
34//!
35//! ```ignore
36//! import! { pub test_class:
37//!     use [obj basic_oop::obj];
38//!     use std::rc::Rc;
39//!     use std::rc::Weak as rc_Weak;
40//! }
41//! ```
42//! 
43//! Now we can start to define our class using [`class_unsafe`] macro.
44//!
45//! Classes defined with the [`class_unsafe`] macro are intended for use either with the [`Rc`](alloc::rc::Rc)
46//! smart pointer or with the [`Arc`](alloc::sync::Arc) smart pointer.
47//! The [`Obj`](obj::Obj) class is the only class indifferent to the type of smart
48//! pointer.
49//!
50//! Suppose we don't need reference counter atomicity. Then our class
51//! definition will be the next:
52//!
53//! ```ignore
54//! #[class_unsafe(inherits_Obj)]
55//! pub struct TestClass { }
56//! ```
57//!
58//! Each class should have two constructors: one for creating this particular class
59//! and one for calling it from the constructor of the inheritor:
60//!
61//! ```ignore
62//! impl TestClass {
63//!     pub fn new() -> Rc<dyn TTestClass> {
64//!         Rc::new(unsafe { Self::new_raw(TEST_CLASS_VTABLE.as_ptr()) })
65//!     }
66//!
67//!     pub unsafe fn new_raw(vtable: Vtable) -> Self {
68//!         TestClass { obj: unsafe { Obj::new_raw(vtable) } }
69//!     }
70//! }
71//! ```
72//!
73//! # Fields
74//!
75//! To add a field to class, we just write it in ordinar way:
76//!
77//! ```ignore
78//! #[class_unsafe(inherits_Obj)]
79//! pub struct TestClass {
80//!     field: RefCell<Rc<String>>,
81//! }
82//!
83//! impl TestClass {
84//!     pub fn new(field: Rc<String>) -> Rc<dyn TTestClass> {
85//!         Rc::new(unsafe { Self::new_raw(field, TEST_CLASS_VTABLE.as_ptr()) })
86//!     }
87//!
88//!     pub unsafe fn new_raw(field: Rc<String>, vtable: Vtable) -> Self {
89//!         TestClass {
90//!             obj: unsafe { Obj::new_raw(vtable) },
91//!             field: RefCell::new(field),
92//!         }
93//!     }
94//! }
95//! ```
96//!
97//! # Non-virtual methods
98//!
99//! To add a method, it is needed to specify a fictive field with `#[non_virt]` attribute and
100//! function type:
101//!
102//! ```ignore
103//! #[class_unsafe(inherits_Obj)]
104//! pub struct TestClass {
105//!     ...
106//!     #[non_virt]
107//!     get_field: fn() -> Rc<String>,
108//! }
109//! ```
110//!
111//! Then `TestClassExt` extension trait will be generated contained appropriate function calling
112//! `TestClass::get_field_impl`. We must provide this implementing function:
113//!
114//! ```ignore
115//! impl TestClass {
116//!     fn get_field_impl(this: &Rc<dyn TTestClass>) -> Rc<String> {
117//!         this.test_class().field.borrow().clone()
118//!     }
119//! }
120//! ```
121//!
122//! # Virtual methods
123//!
124//! Adding a virtual method is no different from adding a non-virtual method only this time
125//! we use `virt`:
126//!
127//! ```ignore
128//! #[class_unsafe(inherits_Obj)]
129//! pub struct TestClass {
130//!     ...
131//!     #[virt]
132//!     set_field: fn(value: Rc<String>),
133//! }
134//!
135//! impl TestClass {
136//!     fn set_field_impl(this: &Rc<dyn TTestClass>, value: Rc<String>) {
137//!         *this.test_class().field.borrow_mut() = value;
138//!     }
139//! }
140//! ```
141//!
142//! # Derived class. Method overriding.
143//!
144//! Lets import our class and derive another one from it:
145//!
146//! ```ignore
147//! import! { pub derived_class:
148//!     use [test_class crate::test_class];
149//! }
150//!
151//! #[class_unsafe(inherits_TestClass)]
152//! pub struct DerivedClass {
153//! }
154//! ```
155//!
156//! Now we wants to override `set_field`, how we do it? Simple:
157//!
158//! ```ignore
159//! #[class_unsafe(inherits_TestClass)]
160//! pub struct DerivedClass {
161//!     #[over]
162//!     set_field: (),
163//! }
164//!
165//! impl DerivedClass {
166//!     pub fn set_field_impl(this: &Rc<dyn TTestClass>, value: Rc<String>) {
167//!         let value = /* coerce value */;
168//!         TestClass::set_field_impl(this, value);
169//!     }
170//! }
171//! ```
172//!
173//! The type of the overridden function is already known from the base class definition,
174//! so there is no need to re-write it, which is why the type of the phony field is specified as `()`.
175//!
176//! # Using the class.
177//!
178//! We can create instance of derived class:
179//!
180//! ```ignore
181//! let class = DerivedClass::new(Rc::new("initial".to_string()));
182//! ```
183//!
184//! We can cast it to base class:
185//!
186//! ```ignore
187//! let base_class: Rc<dyn TTestClass> = dyn_cast_rc(class).unwrap();
188//! ```
189//!
190//! We can call both virtual and non-virtual methods:
191//!
192//! ```ignore
193//! assert_eq!(base_class.get_field().as_ref(), "initial");
194//! base_class.set_field(Rc::new("changed".to_string()));
195//! assert_eq!(base_class.get_field().as_ref(), "changed coerced");
196//! ```
197//!
198//! See full working example in README.md
199//!
200//! # Arc-based classes
201//!
202//! To get a class based on [`Arc`](alloc::sync::Arc) instead of [`Rc`](alloc::rc::Rc),
203//! use the following form of inheritance from [`Obj`](obj::Obj):
204//!
205//! ```ignore
206//! #[class_unsafe(inherits_Obj_sync)]
207//! pub struct TestClass { }
208//! ```
209
210#![no_std]
211
212extern crate alloc;
213
214#[doc=include_str!("../README.md")]
215type _DocTestReadme = ();
216
217#[doc(hidden)]
218pub use macro_magic;
219
220/// Generates class and appropriate helper types and traits.
221///
222/// Usage:
223///
224/// ```ignore
225/// #[class_unsafe(inherits_ParentClass)]
226/// struct Class {
227///     field: FieldType,
228///     #[non_virt]
229///     non_virtual_method: fn(args: ArgsType) -> ResultType,
230///     #[virt]
231///     virtual_method: fn(args: ArgsType) -> ResultType,
232///     #[over]
233///     parent_virtual_method: (),
234/// }
235///
236/// impl Class {
237///     fn non_virtual_method_impl(this: Rc<dyn TClass>, args: ArgsType) -> ResultType {
238///         ...
239///     }
240///
241///     fn virtual_method_impl(this: Rc<dyn TClass>, args: ArgsType) -> ResultType {
242///         ...
243///     }
244///
245///     fn parent_virtual_method_impl(this: Rc<dyn TParentClass>, args: ArgsType) -> ResultType {
246///         let base_result = ParentClass::parent_virtual_method_impl(this, args);
247///         ...
248///     }
249/// }
250/// ```
251///
252/// For a more detailed overview, please refer to the crate documentation.
253///
254/// # Safety
255///
256/// This macro may produce unsound code if the argument to the macro differs
257/// from `inherits_Obj`/`inherits_Obj_sync` [`import`]ed from the [`obj`] module
258/// and another `inherits_...` generated by this macro for a direct or indirect inheritor of `Obj`.
259///
260/// In other words, it is safe to use this macro as long as you
261/// don't try to manually create something that this macro will accept as a valid argument.
262///
263/// It must also be guaranteed that the generated class cannot be constructed with an invalid vtable,
264/// that is, any vtable other than the one generated by this macro
265/// for this class or its direct or indirect inheritor.
266///
267/// This is usually achieved by providing two constructors:
268/// a safe one that creates a class with a precisely known, guaranteed safe table,
269/// and an unsafe one for inheritors that enforces the specified requirement. For example:
270///
271/// ```ignore
272/// #[class_unsafe(inherits_SomeBaseClass)]
273/// pub struct SomeClass { }
274///
275/// impl SomeClass {
276///     pub fn new(param: ParamType) -> Rc<dyn TSomeClass> {
277///         Rc::new(unsafe { Self::new_raw(param, SOME_CLASS_VTABLE.as_ptr()) })
278///     }
279///
280///     pub unsafe fn new_raw(param: ParamType, vtable: Vtable) -> Self {
281///         SomeClass { some_base_class: unsafe { SomeBaseClass::new_raw(param, vtable) } }
282///     }
283/// }
284/// ```
285pub use basic_oop_macro::class_unsafe;
286
287#[doc(hidden)]
288pub use alloc::rc::Rc as alloc_rc_Rc;
289#[doc(hidden)]
290pub use alloc::sync::Arc as alloc_sync_Arc;
291#[doc(hidden)]
292pub use core::mem::transmute as core_mem_transmute;
293#[doc(hidden)]
294pub use dynamic_cast::SupportsInterfaces as dynamic_cast_SupportsInterfaces;
295#[doc(hidden)]
296pub use dynamic_cast::impl_supports_interfaces as dynamic_cast_impl_supports_interfaces;
297#[doc(hidden)]
298pub use dynamic_cast::dyn_cast_rc as dynamic_cast_dyn_cast_rc;
299#[doc(hidden)]
300pub use dynamic_cast::dyn_cast_arc as dynamic_cast_dyn_cast_arc;
301
302/// The pointer to the table containing pointers to class virtual functions.
303///
304/// Use [`class_unsafe`] macro to generate `Vtable`.
305pub type Vtable = *const *const ();
306
307#[doc(hidden)]
308#[repr(C)]
309pub struct VtableJoin<const A: usize, const B: usize> {
310    pub a: [*const (); A],
311    pub b: [*const (); B],
312}
313
314/// Imports base class into the current scope so that it can be inherited from.
315///
316/// The macro accepts input in the following form:
317///
318/// ```ignore
319/// $vis:vis $class:ident :
320/// use $([$base:ident $path:path])+ ;
321/// $( $(#[$attr:meta])* use $($custom_use:tt)+ ; )*
322/// ```
323///
324/// See module documentation for explanation how to use it.
325#[macro_export]
326macro_rules! import {
327    (
328        $vis:vis $class:ident :
329        use $([$base:ident $($path:tt)*])+ ;
330        $($custom_use:tt)*
331    ) => {
332        $vis mod ${concat($class, _types)} {
333            //! Some reexported types and other things.
334            //!
335            //! Used by the [`import`] macro, not intended for direct use in code.
336            $(
337                #[allow(unused_imports)]
338                pub use $($path)*::*;
339                #[allow(unused_imports)]
340                pub use $($path)*:: ${concat($base, _types)} ::*;
341            )+
342            $crate::import_impl! { @split [] [$($custom_use)*] }
343        }
344        use ${concat($class, _types)} ::*;
345    };
346}
347
348#[doc(hidden)]
349#[macro_export]
350macro_rules! import_impl {
351    (
352        @split [$($t:tt)*] [ ; $($custom_use:tt)*]
353    ) => {
354        $crate::import_impl! { @use [$($t)* ; ] }
355        $crate::import_impl! { @split [] [$($custom_use)*] }
356    };
357    (
358        @split [$($t:tt)*] [$x:tt $($custom_use:tt)*]
359    ) => {
360        $crate::import_impl! { @split [$($t)* $x] [$($custom_use)*] }
361    };
362    (
363        @split [] []
364    ) => {
365    };
366    (
367        @split [$($t:tt)+] []
368    ) => {
369        $crate::import_impl! { @use [$($t)+] }
370    };
371    (
372        @use [$(#[$attr:meta])* use $($list:tt)*]
373    ) => {
374        $(#[$attr])*
375        pub use $($list)*
376    };
377}
378
379pub mod obj {
380    use alloc::rc::Rc;
381    use alloc::sync::Arc;
382    use dynamic_cast::{SupportsInterfaces, impl_supports_interfaces};
383    use macro_magic::export_tokens_no_emit;
384    use crate::Vtable;
385
386    pub mod obj_types {
387        //! Some reexported types and other things.
388        //!
389        //! Used by the [`import`] macro, not intended for direct use in code.
390    }
391
392    #[export_tokens_no_emit]
393    #[non_sync]
394    struct inherits_Obj { __class__: Obj }
395
396    #[export_tokens_no_emit]
397    #[sync]
398    struct inherits_Obj_sync { __class__: Obj }
399
400    /// Base class, contains no fields or methods.
401    ///
402    /// Use [`import`] and [`class_unsafe`](crate::class_unsafe) macros
403    /// to define a class inherited from `Obj`:
404    ///
405    /// ```ignore
406    /// #[class_unsafe(inherits_Obj)]
407    /// struct Class { }
408    /// ```
409    ///
410    /// for [`Rc`]-based class, or
411    ///
412    /// ```ignore
413    /// #[class_unsafe(inherits_Obj_sync)]
414    /// struct Class { }
415    /// ```
416    ///
417    /// for [`Arc`]-based one.
418    #[derive(Debug, Clone)]
419    pub struct Obj {
420        vtable: Vtable,
421    }
422
423    unsafe impl Send for Obj { }
424
425    unsafe impl Sync for Obj { }
426
427    impl Obj {
428        /// Creates new `Obj` class instance, wrapped in [`Rc`] smart pointer.
429        ///
430        /// A rarely used function, since it creates `Obj` itself, not one of its inheritors.
431        #[allow(clippy::new_ret_no_self)]
432        pub fn new() -> Rc<dyn TObj> {
433            Rc::new(unsafe { Self::new_raw(OBJ_VTABLE.as_ptr()) })
434        }
435
436        /// Creates new `Obj` class instance, wrapped in [`Arc`] smart pointer.
437        ///
438        /// A rarely used function, since it creates `Obj` itself, not one of its inheritors.
439        pub fn new_sync() -> Arc<dyn TObj> {
440            Arc::new(unsafe { Self::new_raw(OBJ_VTABLE.as_ptr()) })
441        }
442
443        /// Creates new `Obj`.
444        ///
445        /// Intended to be called from inheritors constructors to initialize a base type field.
446        ///
447        /// # Safety
448        ///
449        /// Calling this function is safe iff vtable is empty or
450        /// generated using the [`class_unsafe`](crate::class_unsafe) macro on a
451        /// direct or indirect `Obj` inheritor.
452        pub unsafe fn new_raw(vtable: Vtable) -> Self {
453            Obj { vtable }
454        }
455
456        /// Returns vtable, passed to the constructor.
457        pub fn vtable(&self) -> Vtable { self.vtable }
458    }
459
460    /// Represents [`Obj`] or any of its inheritors.
461    ///
462    /// Usually obtained by using
463    /// [`dyn_cast_rc`](dynamic_cast::dyn_cast_rc)/[`dyn_cast_arc`](dynamic_cast::dyn_cast_arc)
464    /// on a derived trait.
465    pub trait TObj: SupportsInterfaces {
466        /// Returns reference to inner data.
467        fn obj(&self) -> &Obj;
468    }
469
470    impl_supports_interfaces!(Obj: TObj);
471
472    impl TObj for Obj {
473        fn obj(&self) -> &Obj { self }
474    }
475
476    /// [`Obj`] virtual methods list.
477    ///
478    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
479    #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
480    #[repr(usize)]
481    pub enum ObjVirtMethods {
482        VirtMethodsCount = 0usize,
483    }
484
485    /// [`Obj`] virtual methods table.
486    ///
487    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
488    pub struct ObjVtable(pub [*const (); ObjVirtMethods::VirtMethodsCount as usize]);
489
490    impl ObjVtable {
491        /// Creates [`Obj`] virtual methods table.
492        ///
493        /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
494        #[allow(clippy::new_without_default)]
495        pub const fn new() -> Self {
496            ObjVtable([])
497        }
498    }
499
500    const OBJ_VTABLE: [*const (); ObjVirtMethods::VirtMethodsCount as usize] = ObjVtable::new().0;
501}