memflex 0.8.4

Memory hacking library
Documentation
/// Generates a trait that will emulate behaviour of C++ virtual functions
/// # Safety
/// Although functions generated by this macro are not marked as unsafe, they will
/// cause access violation if called on invalid objects.
/// ```
/// /// The target struct cannot be zero sized!
/// #[repr(C)]
/// pub struct ConcreteType(usize);
///
/// memflex::interface! {
///     pub trait IPlayer impl for ConcreteType /*, OtherTypes, ... */ {
///         // Notice missing `&self`, this is intentional and macro will implicitly add it.
///         // Functions without `&self` in interface don't make much sense.
///         extern fn get_health() -> i32 = #0; // 0 - Index of the virtual function.
///         // *Returns old health*
///         extern "C" fn set_health(new: i32) -> i32 = #1; // 1 - Index of the virtual function.
///     }
/// }
///
/// /* C++ Code
/// class IPlayer {
/// public:
///     virtual int get_health();
///     virtual int set_health(int new);
/// }
/// */
/// ```
#[macro_export]
macro_rules! interface {
    {
        $(
            $vs:vis trait $iname:ident $(impl for $($implt:ty),* )? {
                $(
                    $(extern $($abi:literal)?)? fn $fname:ident( $($arg_name:ident: $arg_ty:ty),* )
                        $(-> $ret:ty)? = $sep:tt $idx:expr $(, $mod_name:literal)?;
                )*
            }
        )*
    } => {
        $(
            #[allow(clippy::missing_safety_doc)]
            $vs unsafe trait $iname: Sized {
                const INDEX_OFFSET: usize = 0;
                const FUNCTION_COUNT: usize = $( $crate::__count_fns!($fname) + )*0;

                $(

                    $crate::__gen_func! {
                        $sep
                        [$($mod_name)? | $idx],
                        $(extern $($abi)?)?,
                        $fname,
                        ($($arg_name, $arg_ty),* ),
                        $($ret)?
                    }
                )*
            }

            $(
                $(
                    unsafe impl $iname for $implt { }
                )*
            )?
        )*
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __gen_func {
    (# [| $idx:expr], $(extern $($abi:literal)?)?, $fname:ident, ( $($arg_name:ident, $arg_ty:ty),* ), $($ret:ty)? ) => {
        $(extern $($abi)?)? fn $fname<'this>(&'this self, $($arg_name: $arg_ty),* ) $(-> $ret)? {
            unsafe {
                type Fn<'this> = $(extern $($abi)?)? fn(*const (), $($arg_ty),*) $(-> $ret)?;

                #[allow(clippy::useless_transmute)]
                ((**core::mem::transmute::<_, *const *const [Fn<'this>; $idx + 1]>(self))[Self::INDEX_OFFSET + $idx])(
                    self as *const Self as _,
                    $($arg_name),*
                )
            }
        }
    };
    (% [$mod_name:literal | $pattern:expr], $(extern $($abi:literal)?)?, $fname:ident, ( $($arg_name:ident, $arg_ty:ty),* ), $($ret:ty)? ) => {
        $(extern $($abi)?)? fn $fname<'this>(&'this self, $($arg_name: $arg_ty),* ) $(-> $ret)? {
            unsafe {
                type Fn = $(extern $($abi)?)? fn(*const (), $($arg_ty),*) $(-> $ret)?;

                $crate::function! {
                    $(extern $($abi)?)? fn __CALLE(usize, $($arg_ty),*) $(-> $ret)? = $mod_name%$pattern;
                }

                __CALLE(self as *const Self as usize, $($arg_name),*)
            }
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __count_fns {
    ($a:ident) => {
        1
    };
}