Search code examples
rustmacrospyo3rust-decl-macros

How do I generate a macro from previous macro-generated code?


In order to create a pyo3-powered Python class working with a struct that uses generic type, I want to use wrappers that will generate the code needed to not have to do it for every specific type.

I created a macro that generates the code but I need to register the functions generated by my macro as functions of the Python module.

One way would be to keep track of the idents used in the macro to use them and generate the wrap_pyfunction with another macro, but I cannot find anything related.

(Of course any other solution to generate the code will be welcomed warmly)

The (simplified) code I have right now:

macro_rules! create_python_function {
    ($name:ident, $objtype:expr) => {
        paste!{
            #[pyclass(unsendable)]
            pub struct [<$name PyIface>] {
                obj: GenericStruct<$objtype>,
            }

            impl [<$name PyIface>]{
                pub fn new() -> [<$name PyIface>]{
                    [<$name PyIface>] {}
                }
            }

            pub fn [<create_object_ $name>]() -> [<$name PyIface>]{
                [<$name PyIface>]::new()
            }
        }
    };
}

create_python_function!(name, SpecificType);

#[pymodule]
fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> {
    /* I want to auto-generate this with macro
     * m.add_function(wrap_pyfunction!(create_object_name, m)?).unwrap(); 
     */
    Ok(())
}

Solution

  • Macros cannot share their args or states. If you don't want to repeat identifiers, move the mymodule definition into create_python_function macro and change the macro to use repetitions (The Rust Reference)

    macro_rules! create_python_function {
        ($($name:ident => $objtype:ty),* $(,)?) => {
            $(
                paste! {
                    #[pyclass(unsendable)]
                    pub struct [<$name PyIface>] {
                        obj: GenericStruct<$objtype>,
                    }
    
                    impl [<$name PyIface>]{
                        pub fn new() -> [<$name PyIface>]{
                            [<$name PyIface>] { obj: todo!() }
                        }
                    }
    
                    pub fn [<create_object_ $name>]() -> [<$name PyIface>]{
                        [<$name PyIface>]::new()
                    }
                }
            )*
    
            #[pymodule]
            fn mymodule(_py: Python, m: &PyModule) -> Result<(), ()> {
                $(
                    paste! {
                        m.add_function(wrap_pyfunction!([<create_object_ $name>], m)?).unwrap();
                    }
                )*
                Ok(())
            }
        };
    }
    
    struct Foo;
    create_python_function!(
        foo => Foo,
        v => Vec<()>,
    );