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//!
48//! Suppose we don't need reference counter atomicity. Then our class
49//! definition will be the next:
50//!
51//! ```ignore
52//! #[class_unsafe(inherits_Obj)]
53//! pub struct TestClass { }
54//! ```
55//!
56//! Each class should have two constructors: one for creating this particular class
57//! and one for calling it from the constructor of the inheritor:
58//!
59//! ```ignore
60//! impl TestClass {
61//!     pub fn new() -> Rc<dyn IsTestClass> {
62//!         Rc::new(unsafe { Self::new_raw(TEST_CLASS_VTABLE.as_ptr()) })
63//!     }
64//!
65//!     pub unsafe fn new_raw(vtable: Vtable) -> Self {
66//!         TestClass { obj: unsafe { Obj::new_raw(vtable) } }
67//!     }
68//! }
69//! ```
70//!
71//! # Fields
72//!
73//! To add a field to class, we just write it in ordinar way:
74//!
75//! ```ignore
76//! #[class_unsafe(inherits_Obj)]
77//! pub struct TestClass {
78//!     field: RefCell<Rc<String>>,
79//! }
80//!
81//! impl TestClass {
82//!     pub fn new(field: Rc<String>) -> Rc<dyn IsTestClass> {
83//!         Rc::new(unsafe { Self::new_raw(field, TEST_CLASS_VTABLE.as_ptr()) })
84//!     }
85//!
86//!     pub unsafe fn new_raw(field: Rc<String>, vtable: Vtable) -> Self {
87//!         TestClass {
88//!             obj: unsafe { Obj::new_raw(vtable) },
89//!             field: RefCell::new(field),
90//!         }
91//!     }
92//! }
93//! ```
94//!
95//! # Non-virtual methods
96//!
97//! To add a method, it is needed to specify a fictive field with `#[non_virt]` attribute and
98//! function type:
99//!
100//! ```ignore
101//! #[class_unsafe(inherits_Obj)]
102//! pub struct TestClass {
103//!     ...
104//!     #[non_virt]
105//!     get_field: fn() -> Rc<String>,
106//! }
107//! ```
108//!
109//! Then `TestClassExt` extension trait will be generated contained appropriate function calling
110//! `TestClass::get_field_impl`. We must provide this implementing function:
111//!
112//! ```ignore
113//! impl TestClass {
114//!     fn get_field_impl(this: &Rc<dyn IsTestClass>) -> Rc<String> {
115//!         this.test_class().field.borrow().clone()
116//!     }
117//! }
118//! ```
119//!
120//! # Virtual methods
121//!
122//! Adding a virtual method is no different from adding a non-virtual method only this time
123//! we use `virt`:
124//!
125//! ```ignore
126//! #[class_unsafe(inherits_Obj)]
127//! pub struct TestClass {
128//!     ...
129//!     #[virt]
130//!     set_field: fn(value: Rc<String>),
131//! }
132//!
133//! impl TestClass {
134//!     fn set_field_impl(this: &Rc<dyn IsTestClass>, value: Rc<String>) {
135//!         *this.test_class().field.borrow_mut() = value;
136//!     }
137//! }
138//! ```
139//!
140//! # Derived class. Method overriding.
141//!
142//! Lets import our class and derive another one from it:
143//!
144//! ```ignore
145//! import! { pub derived_class:
146//!     use [test_class crate::test_class];
147//! }
148//!
149//! #[class_unsafe(inherits_TestClass)]
150//! pub struct DerivedClass {
151//! }
152//! ```
153//!
154//! Now we wants to override `set_field`, how we do it? Simple:
155//!
156//! ```ignore
157//! #[class_unsafe(inherits_TestClass)]
158//! pub struct DerivedClass {
159//!     #[over]
160//!     set_field: (),
161//! }
162//!
163//! impl DerivedClass {
164//!     pub fn set_field_impl(this: &Rc<dyn IsTestClass>, value: Rc<String>) {
165//!         let value = /* coerce value */;
166//!         TestClass::set_field_impl(this, value);
167//!     }
168//! }
169//! ```
170//!
171//! The type of the overridden function is already known from the base class definition,
172//! so there is no need to re-write it, which is why the type of the phony field is specified as `()`.
173//!
174//! # Using the class.
175//!
176//! We can create instance of derived class:
177//!
178//! ```ignore
179//! let class = DerivedClass::new(Rc::new("initial".to_string()));
180//! ```
181//!
182//! We can cast it to base class:
183//!
184//! ```ignore
185//! let base_class: Rc<dyn IsTestClass> = dyn_cast_rc(class).unwrap();
186//! ```
187//!
188//! We can call both virtual and non-virtual methods:
189//!
190//! ```ignore
191//! assert_eq!(base_class.get_field().as_ref(), "initial");
192//! base_class.set_field(Rc::new("changed".to_string()));
193//! assert_eq!(base_class.get_field().as_ref(), "changed coerced");
194//! ```
195//!
196//! See full working example in README.md
197//!
198//! # Arc-based classes
199//!
200//! To get a class based on [`Arc`](alloc::sync::Arc) instead of [`Rc`](alloc::rc::Rc),
201//! use use [`ObjSync`](obj_sync::ObjSync) instead of [`Obj`](obj::Obj):
202//!
203//! ```ignore
204//! import! { pub test_class:
205//!     use [obj_sync basic_oop::obj_sync];
206//!     ...
207//! }
208//! #[class_unsafe(inherits_ObjSync)]
209//! pub struct TestClass { }
210//! ```
211
212#![no_std]
213
214extern crate alloc;
215
216#[doc=include_str!("../README.md")]
217type _DocTestReadme = ();
218
219#[doc(hidden)]
220pub use macro_magic;
221
222/// Generates class and appropriate helper types and traits.
223///
224/// Usage:
225///
226/// ```ignore
227/// #[class_unsafe(inherits_ParentClass)]
228/// struct Class {
229///     field: FieldType,
230///     #[non_virt]
231///     non_virtual_method: fn(args: ArgsType) -> ResultType,
232///     #[virt]
233///     virtual_method: fn(args: ArgsType) -> ResultType,
234///     #[over]
235///     parent_virtual_method: (),
236/// }
237///
238/// impl Class {
239///     fn non_virtual_method_impl(this: Rc<dyn IsClass>, args: ArgsType) -> ResultType {
240///         ...
241///     }
242///
243///     fn virtual_method_impl(this: Rc<dyn IsClass>, args: ArgsType) -> ResultType {
244///         ...
245///     }
246///
247///     fn parent_virtual_method_impl(this: Rc<dyn IsParentClass>, args: ArgsType) -> ResultType {
248///         let base_result = ParentClass::parent_virtual_method_impl(this, args);
249///         ...
250///     }
251/// }
252/// ```
253///
254/// For a more detailed overview, please refer to the crate documentation.
255///
256/// # Safety
257///
258/// This macro may produce unsound code if the argument to the macro differs
259/// from `inherits_Obj`/`inherits_Obj_sync` [`import`]ed from the [`obj`] module
260/// and another `inherits_...` generated by this macro for a direct or indirect inheritor of `Obj`.
261///
262/// In other words, it is safe to use this macro as long as you
263/// don't try to manually create something that this macro will accept as a valid argument.
264///
265/// It must also be guaranteed that the generated class cannot be constructed with an invalid vtable,
266/// that is, any vtable other than the one generated by this macro
267/// for this class or its direct or indirect inheritor.
268///
269/// This is usually achieved by providing two constructors:
270/// a safe one that creates a class with a precisely known, guaranteed safe table,
271/// and an unsafe one for inheritors that enforces the specified requirement. For example:
272///
273/// ```ignore
274/// #[class_unsafe(inherits_SomeBaseClass)]
275/// pub struct SomeClass { }
276///
277/// impl SomeClass {
278///     pub fn new(param: ParamType) -> Rc<dyn IsSomeClass> {
279///         Rc::new(unsafe { Self::new_raw(param, SOME_CLASS_VTABLE.as_ptr()) })
280///     }
281///
282///     pub unsafe fn new_raw(param: ParamType, vtable: Vtable) -> Self {
283///         SomeClass { some_base_class: unsafe { SomeBaseClass::new_raw(param, vtable) } }
284///     }
285/// }
286/// ```
287pub use basic_oop_macro::class_unsafe;
288
289#[doc(hidden)]
290pub use alloc::rc::Rc as alloc_rc_Rc;
291#[doc(hidden)]
292pub use alloc::sync::Arc as alloc_sync_Arc;
293#[doc(hidden)]
294pub use core::mem::transmute as core_mem_transmute;
295#[doc(hidden)]
296pub use dynamic_cast::SupportsInterfaces as dynamic_cast_SupportsInterfaces;
297#[doc(hidden)]
298pub use dynamic_cast::impl_supports_interfaces as dynamic_cast_impl_supports_interfaces;
299#[doc(hidden)]
300pub use dynamic_cast::dyn_cast_rc as dynamic_cast_dyn_cast_rc;
301#[doc(hidden)]
302pub use dynamic_cast::dyn_cast_arc as dynamic_cast_dyn_cast_arc;
303
304/// The pointer to the table containing pointers to class virtual functions.
305///
306/// Use [`class_unsafe`] macro to generate `Vtable`.
307pub type Vtable = *const *const ();
308
309#[doc(hidden)]
310#[repr(C)]
311pub struct VtableJoin<const A: usize, const B: usize> {
312    pub a: [*const (); A],
313    pub b: [*const (); B],
314}
315
316/// Imports base class into the current scope so that it can be inherited from.
317///
318/// The macro accepts input in the following form:
319///
320/// ```ignore
321/// $vis:vis $class:ident :
322/// use $([$base:ident $path:path])+ ;
323/// $( $(#[$attr:meta])* use $($custom_use:tt)+ ; )*
324/// ```
325///
326/// See module documentation for explanation how to use it.
327#[macro_export]
328macro_rules! import {
329    (
330        $vis:vis $class:ident :
331        use $([$base:ident $($path:tt)*])+ ;
332        $($custom_use:tt)*
333    ) => {
334        $vis mod ${concat($class, _types)} {
335            //! Some reexported types and other things.
336            //!
337            //! Used by the [`import`] macro, not intended for direct use in code.
338            $(
339                #[allow(unused_imports)]
340                pub use $($path)*::*;
341                #[allow(unused_imports)]
342                pub use $($path)*:: ${concat($base, _types)} ::*;
343            )+
344            $crate::import_impl! { @split [] [$($custom_use)*] }
345        }
346        use ${concat($class, _types)} ::*;
347    };
348}
349
350#[doc(hidden)]
351#[macro_export]
352macro_rules! import_impl {
353    (
354        @split [$($t:tt)*] [ ; $($custom_use:tt)*]
355    ) => {
356        $crate::import_impl! { @use [$($t)* ; ] }
357        $crate::import_impl! { @split [] [$($custom_use)*] }
358    };
359    (
360        @split [$($t:tt)*] [$x:tt $($custom_use:tt)*]
361    ) => {
362        $crate::import_impl! { @split [$($t)* $x] [$($custom_use)*] }
363    };
364    (
365        @split [] []
366    ) => {
367    };
368    (
369        @split [$($t:tt)+] []
370    ) => {
371        $crate::import_impl! { @use [$($t)+] }
372    };
373    (
374        @use [$(#[$attr:meta])* use $($list:tt)*]
375    ) => {
376        $(#[$attr])*
377        pub use $($list)*
378    };
379}
380
381pub mod obj {
382    use alloc::rc::Rc;
383    use downcast_rs::{Downcast, impl_downcast};
384    use dynamic_cast::{SupportsInterfaces, impl_supports_interfaces};
385    use macro_magic::export_tokens_no_emit;
386    use crate::Vtable;
387
388    pub mod obj_types {
389        //! Some reexported types and other things.
390        //!
391        //! Used by the [`import`] macro, not intended for direct use in code.
392    }
393
394    #[export_tokens_no_emit]
395    #[non_sync]
396    struct inherits_Obj { __class__: Obj }
397
398    /// Base class, contains no fields or methods. For [`Rc`]-based classes.
399    ///
400    /// Use [`import`] and [`class_unsafe`](crate::class_unsafe) macros
401    /// to define a class inherited from `Obj`:
402    ///
403    /// ```ignore
404    /// #[class_unsafe(inherits_Obj)]
405    /// struct Class { }
406    /// ```
407    #[derive(Debug, Clone)]
408    pub struct Obj {
409        vtable: Vtable,
410    }
411
412    unsafe impl Send for Obj { }
413
414    unsafe impl Sync for Obj { }
415
416    impl Obj {
417        /// Creates new `Obj` class instance, wrapped in [`Rc`] smart pointer.
418        ///
419        /// A rarely used function, since it creates `Obj` itself, not one of its inheritors.
420        #[allow(clippy::new_ret_no_self)]
421        pub fn new() -> Rc<dyn IsObj> {
422            Rc::new(unsafe { Self::new_raw(OBJ_VTABLE.as_ptr()) })
423        }
424
425        /// Creates new `Obj`.
426        ///
427        /// Intended to be called from inheritors constructors to initialize a base type field.
428        ///
429        /// # Safety
430        ///
431        /// Calling this function is safe iff vtable is empty or
432        /// generated using the [`class_unsafe`](crate::class_unsafe) macro on a
433        /// direct or indirect `Obj` inheritor.
434        pub unsafe fn new_raw(vtable: Vtable) -> Self {
435            Obj { vtable }
436        }
437
438        /// Returns vtable, passed to the constructor.
439        pub fn vtable(&self) -> Vtable { self.vtable }
440    }
441
442    /// Represents [`Obj`] or any of its inheritors.
443    ///
444    /// Usually obtained by using
445    /// [`dyn_cast_rc`](dynamic_cast::dyn_cast_rc)
446    /// on a derived trait.
447    pub trait IsObj: SupportsInterfaces + Downcast {
448        /// Returns reference to inner data.
449        fn obj(&self) -> &Obj;
450
451        /// Converts to inner data.
452        fn into_obj(self) -> Obj where Self: Sized;
453    }
454
455    impl_supports_interfaces!(Obj: IsObj);
456
457    impl_downcast!(IsObj);
458
459    impl IsObj for Obj {
460        fn obj(&self) -> &Obj { self }
461
462        fn into_obj(self) -> Obj { self }
463    }
464
465    /// [`Obj`] virtual methods list.
466    ///
467    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
468    #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
469    #[repr(usize)]
470    pub enum ObjVirtMethods {
471        VirtMethodsCount = 0usize,
472    }
473
474    /// [`Obj`] virtual methods table.
475    ///
476    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
477    pub struct ObjVtable(pub [*const (); ObjVirtMethods::VirtMethodsCount as usize]);
478
479    impl ObjVtable {
480        /// Creates [`Obj`] virtual methods table.
481        ///
482        /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
483        #[allow(clippy::new_without_default)]
484        pub const fn new() -> Self {
485            ObjVtable([])
486        }
487    }
488
489    const OBJ_VTABLE: [*const (); ObjVirtMethods::VirtMethodsCount as usize] = ObjVtable::new().0;
490}
491
492pub mod obj_sync {
493    use alloc::sync::Arc;
494    use downcast_rs::{DowncastSync, impl_downcast};
495    use dynamic_cast::{SupportsInterfaces, impl_supports_interfaces};
496    use macro_magic::export_tokens_no_emit;
497    use crate::Vtable;
498
499    pub mod obj_sync_types {
500        //! Some reexported types and other things.
501        //!
502        //! Used by the [`import`] macro, not intended for direct use in code.
503    }
504
505    #[export_tokens_no_emit]
506    #[sync]
507    struct inherits_ObjSync { __class__: ObjSync }
508
509    /// Base class, contains no fields or methods. For [`Arc`]-based classes.
510    ///
511    /// Use [`import`] and [`class_unsafe`](crate::class_unsafe) macros
512    /// to define a class inherited from `ObjSync`:
513    ///
514    /// ```ignore
515    /// #[class_unsafe(inherits_ObjSync)]
516    /// struct Class { }
517    /// ```
518    #[derive(Debug, Clone)]
519    pub struct ObjSync {
520        vtable: Vtable,
521    }
522
523    unsafe impl Send for ObjSync { }
524
525    unsafe impl Sync for ObjSync { }
526
527    impl ObjSync {
528        /// Creates new `ObjSync` class instance, wrapped in [`Arc`] smart pointer.
529        ///
530        /// A rarely used function, since it creates `ObjSync` itself, not one of its inheritors.
531        #[allow(clippy::new_ret_no_self)]
532        pub fn new() -> Arc<dyn IsObjSync> {
533            Arc::new(unsafe { Self::new_raw(OBJ_SYNC_VTABLE.as_ptr()) })
534        }
535
536        /// Creates new `ObjSync`.
537        ///
538        /// Intended to be called from inheritors constructors to initialize a base type field.
539        ///
540        /// # Safety
541        ///
542        /// Calling this function is safe iff vtable is empty or
543        /// generated using the [`class_unsafe`](crate::class_unsafe) macro on a
544        /// direct or indirect `ObjSync` inheritor.
545        pub unsafe fn new_raw(vtable: Vtable) -> Self {
546            ObjSync { vtable }
547        }
548
549        /// Returns vtable, passed to the constructor.
550        pub fn vtable(&self) -> Vtable { self.vtable }
551    }
552
553    /// Represents [`ObjSync`] or any of its inheritors.
554    ///
555    /// Usually obtained by using
556    /// [`dyn_cast_arc`](dynamic_cast::dyn_cast_arc)
557    /// on a derived trait.
558    pub trait IsObjSync: SupportsInterfaces + DowncastSync {
559        /// Returns reference to inner data.
560        fn obj_sync(&self) -> &ObjSync;
561
562        /// Converts to inner data.
563        fn into_obj_sync(self) -> ObjSync where Self: Sized;
564    }
565
566    impl_supports_interfaces!(ObjSync: IsObjSync);
567
568    impl_downcast!(sync IsObjSync);
569
570    impl IsObjSync for ObjSync {
571        fn obj_sync(&self) -> &ObjSync { self }
572
573        fn into_obj_sync(self) -> ObjSync { self }
574    }
575
576    /// [`ObjSync`] virtual methods list.
577    ///
578    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
579    #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
580    #[repr(usize)]
581    pub enum ObjSyncVirtMethods {
582        VirtMethodsCount = 0usize,
583    }
584
585    /// [`ObjSync`] virtual methods table.
586    ///
587    /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
588    pub struct ObjSyncVtable(pub [*const (); ObjSyncVirtMethods::VirtMethodsCount as usize]);
589
590    impl ObjSyncVtable {
591        /// Creates [`ObjSync`] virtual methods table.
592        ///
593        /// Used by the [`class_unsafe`](crate::class_unsafe) macro, not intended for direct use in code.
594        #[allow(clippy::new_without_default)]
595        pub const fn new() -> Self {
596            ObjSyncVtable([])
597        }
598    }
599
600    const OBJ_SYNC_VTABLE: [*const (); ObjSyncVirtMethods::VirtMethodsCount as usize] = ObjSyncVtable::new().0;
601}