How to create macros with same name in different files?

Hi,

In my crate I repeatedly use similar macro names in different files. I have some thing like

mod equations {
#[macro_export]
macro_rules! make_forces_torques_zero{
    (($($dest:ident), *)) => {
        $(
            let d_fx = &mut $dest.fx;
            let d_fy = &mut $dest.fy;
            let d_torz = &mut $dest.torz;

            for i in 0..d_fx.len(){
                d_fx[i] = 0.;
                d_fy[i] = 0.;
                d_torz[i] = 0.;
            }
        )*}
}

mod equations2 {
#[macro_export]
macro_rules! make_forces_torques_zero{
    (($($dest:ident), *)) => {
        $(
            let d_fx = &mut $dest.fx;
            let d_fy = &mut $dest.fy;
            let d_torz = &mut $dest.torz;

            for i in 0..d_fx.len(){
                d_fx[i] = 0.;
                d_fy[i] = 0.;
                d_torz[i] = 0.;
            }
        )*}
}

Now when I try to import them, the compiler says, that

error: a macro named `make_forces_torques_zero` has already been exported
  --> src/obermayr2013/equations_2.rs:2:1
   |
2  | / macro_rules! make_forces_torques_zero{
3  | |     (($($dest:ident), *)) => {
4  | |         $(
5  | |             let d_fx = &mut $dest.fx;
...  |
14 | |         )*}
15 | | }
   | |_^ `make_forces_torques_zero` already exported
   |
   = note: `#[deny(duplicate_macro_exports)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
   = note: for more information, see issue #35896 <https://github.com/rust-lang/rust/issues/35896>
note: previous macro export is now shadowed
  --> src/obermayr2013/equations.rs:2:1
   |
2  | / macro_rules! make_forces_torques_zero{
3  | |     (($($dest:ident), *)) => {
4  | |         $(
5  | |             let d_fx = &mut $dest.fx;
...  |
14 | |         )*}
15 | | }
   | |_^

Is there any way to make this work? Or the only way is to create macros with different names.

Why not just use a function?

fn make_forces_torques_zero(dest: &mut TypeOfYourThing) {
    for ptr in dest.fx.iter_mut() { *ptr = 0.0; }
    for ptr in dest.fy.iter_mut() { *ptr = 0.0; }
    for ptr in dest.torz.iter_mut() { *ptr = 0.0; }
}

Sorry, I can't do that. I have too many types in my program. If I have to write them specific to type then I have to write too many functions or have to derive too many traits.

This way, the macro will take care of expanding it and throws an error if a specific dest doesn't have a certain type. Which can be fixed simply by adding such a attribute to the struct and initializing to zero.

You can find an example here

I am going to write too many schemes in future.

Here is the same implementation but using functions here.

Why do you have several copies of the same macro anyway?

Well actually, I can make them with different names but, I have a problem (beam bending) which can be solved with few implementation (lattice springs, SPH, DEM, V-model), all these have the same force computation equation (or macro).

I can definitely prepend these model names to the macro (but I think eventually it doesn't look good), but do you guys have any other ideas?

Macros unfortunately don't have good namespacing properties. If you want that, you will have to use functions.

That said, I can't find any types besides the Particles type that you are using the macro on. Are you sure it wouldn't be a good idea?

Yes, I am pretty sure. I have written before with functions. Please look at the links of the edited reply
link

I mean I agree that this:

pub fn summation_density(
    d_x: &[f32], d_y: &[f32], d_z: &[f32], d_h: &[f32],
    d_m: &[f32], d_rho: &mut [f32], s_x: &[f32], s_y: &[f32],
    s_z: &[f32], s_nnps_id: usize, nnps: &(dyn NNPSGeneric+ Sync),
    kernel: &(dyn Kernel + Sync))
{
    d_rho.par_iter_mut().enumerate().for_each(|(i, d_rho_i)| {
        let nbrs = nnps.get_neighbours(d_x[i], d_y[i], d_z[i], s_nnps_id);
        for &j in nbrs.iter() {
            let dx = d_x[i] - s_x[j];
            let dy = d_y[i] - s_y[j];
            let dz = d_z[i] - s_z[j];
            let rij = (dx.powf(2.) + dy.powf(2.) + dz.powf(2.)).sqrt();
            let wij = kernel.get_wij(rij, d_h[i]);

            *d_rho_i += d_m[i] * wij;
        }
    });
}

is a bad way to do it.

I wrote some example code shows how to go in the direction of using functions in a less terrible way. I rewrote the normal_force_dem function, as it seemed the most complex in your project.

One thing that sounds like it would be an improvement would be to treat vectors as a single object instead of working directly on coordinates. In the example, I have implemented a rudimentary 2d vector type and used it to compute the vector from the point to its neighbors.

Of course this can still be much improved. Note that one advantage of using functions is that it tells you if you have unused variables. For example I noticed that you never used that d_tz_i value that you zipped into the iterator because of a compiler warning.

pub fn normal_force_dem(particles: &mut Particles, nnps: NBS2D) {
    use rayon::prelude::*;
    
    let x = &particles.x;
    let y = &particles.y;
    let radius = &particles.radius;
    
    particles.fx.par_iter_mut()
      .zip(particles.fy.par_iter_mut().enumerate())
      .for_each(|(d_fx_i, (i, d_fy_i))|
    {
        let nbrs = nnps.get_neighbours(x[i], y[i]);
        for j in nbrs {
            
            let point_coord = Vector2::new(x[i], y[i]);
            let neigh_coord = Vector2::new(x[j], y[j]);
            let delta = neigh_coord - point_coord;
            
            // unit vector passing from d_idx to s_idx
            if let Some(unit_vector) = delta.unit_vector() {
                // find overlap amount
                let overlap = radius[i] + radius[j] - delta.size();
                // println!("distance is {}", dist);
                // println!("overlap is {}", overlap);
                let kn = 1e5;
                
                if overlap > 0. {
                    *d_fx_i -= kn * overlap * unit_vector.x;
                    *d_fy_i -= kn * overlap * unit_vector.y;
                    // This could have been:
                    // *force -= kn * overlap * unit_vector;
                }
            }
        }
    });
}

playground

Also you're not coding in C89. You're allowed, encouraged even, to declare variables at use instead of at the top.

2 Likes

Ignoring XY problem and just answering the OP question: macros defined in the current crate get #[macro_export]-ed at the root namespace, name-clashing as you noticed.

There is a hacky way to go around this limitation:

  1. Define your macros in a new crate, that your current crate will depend on (i.e., a helper / internals crate), using custom internal names that do not clash / collide with each other.

    For instance, in your ::__helper__ crate you define:

    • equations_make_forces_torques_zero!, and

    • equations2_make_forces_torques_zero!

  2. Within your main crate, you just reexport these macros from the right modules and with the option to rename them as you please:

    pub
    mod equations {
        pub
        use ::__helper__::equations_make_forces_torques_zero
            as make_forces_torques_zero
        ;
    }
    
    pub
    mod equations2 {
        pub
        use ::__helper__::equations2_make_forces_torques_zero
            as make_forces_torques_zero
        ;
    }
    
1 Like

Thanks for the efforts. I have a question here, I was defining the variables at the top because I am inside the for loop, so by defining them mutably at the top, helps not redefining them in the loop, does this makes sense?

Thanks for the answer. Looks like there is no way of doing it now in rust. May be future.

You should just redefine them in the loop.

You can do this with a single crate as well! You just have to live with both having names at the root and reexported at the module path.

pub mod a {
    #[macro_export] #[doc(hidden)]
    macro_rules! __a__make_forces { }
    #[doc(inline)]
    pub use __a__make_forces as make_forces;
}

pub mod b {
    #[macro_export] #[doc(hidden)]
    macro_rules! __b__make_forces { }
    #[doc(inline)]
    pub use __b__make_forces as make_forces;
}

If the macro just has to be crate-local, you can skip the #[macro_export] and do pub(crate) use. (Disclaimer: while I've used this technique before I don't know how it interacts with docs, as I used this for macro-generated macros that didn't have docs.)

3 Likes

Good to know!

Here is the result for v1.40.0 :grimacing:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.