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 IsTestClass> {
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 IsTestClass> {
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 IsTestClass>) -> 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 IsTestClass>, 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 IsTestClass>, 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 IsTestClass> = 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 IsClass>, args: ArgsType) -> ResultType {
238/// ...
239/// }
240///
241/// fn virtual_method_impl(this: Rc<dyn IsClass>, args: ArgsType) -> ResultType {
242/// ...
243/// }
244///
245/// fn parent_virtual_method_impl(this: Rc<dyn IsParentClass>, 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 IsSomeClass> {
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 IsObj> {
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 IsObj> {
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 IsObj: SupportsInterfaces {
466 /// Returns reference to inner data.
467 fn obj(&self) -> &Obj;
468 }
469
470 impl_supports_interfaces!(Obj: IsObj);
471
472 impl IsObj 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}