1

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(())
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
litchipi
  • 53
  • 1
  • 5
  • You can define macro in macro definitions and it will be available after a call of the outer macro. The inner macro definition can use arguments of the outer one. Though I'm unsure if this is what you need. – Dawer May 23 '21 at 20:24
  • No, yet it was interesting thank you. What I want is to call a bunch of "create_python_function" macro, and call only one "wrap_created_python_functions" in the pymodule that would generate 1 line of code for each ident of the "create_python_function" I called before. I need to memorize all the $name ident used in the create_python_function macro and use it to generate the final wrapping (I hope I'm clear enough) – litchipi May 25 '21 at 19:12

1 Answers1

1

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<()>,
);
Dawer
  • 309
  • 1
  • 6
  • Thanks a lot, elegant way to deal with this problem indeed ! Rust macros are so powerful and I expected I missed something like this – litchipi May 26 '21 at 12:02