Generate function with specified args using macro


#1

Hi I’m new here and to using Rust.

I’m building an FFI library for noise-rs for the purpose of linking to LuaJIT.

Is there any way to create a macro that generates a function with specified arguments?

An example using the hypothetical macro to generate different functions based on the amount of dimensions.

wrap_noise!(noise_perlin2, c_double, noise::perlin2, 2);
//          ^[1]           ^[2]      ^[3]            ^[4]
/*
    [1]: The new function name.
    [2]: The type of the x, y, z, etc. parameters and the output
    [3]: The function to be wrapped.
    [4]: Number of dimensions
 */

Generates:

pub unsafe fn noise_perlin2(seed: *mut noise::Seed, x: c_double, y: c_double) -> c_double {
    let seed: &noise::Seed = &*seed;
    noise::perlin2(&seed, &[x, y])
}

Currently I have one macro for every number of dimensions.

/// Wrap 2D noise function.
macro_rules! wrap_noise2 {
    ( $wrapper_name:ident, $t:ty, $alg:expr ) => {
        #[no_mangle]
        pub unsafe fn $wrapper_name(seed: *mut noise::Seed, x: $t, y: $t) -> $t {
            let seed: &noise::Seed = &*seed;
            $alg(&seed, &[x, y])
        }
    }
}

/// Wrap 3D noise function.
macro_rules! wrap_noise3 {
    ( $wrapper_name:ident, $t:ty, $alg:expr ) => {
        #[no_mangle]
        pub unsafe fn $wrapper_name(seed: *mut noise::Seed, x: $t, y: $t, z: $t) -> $t {
            let seed: &noise::Seed = &*seed;
            $alg(&seed, &[x, y, z])
        }
    }
}

/// Wrap 4D noise function.
macro_rules! wrap_noise4 {
    ( $wrapper_name:ident, $t:ty, $alg:expr ) => {
        #[no_mangle]
        pub unsafe fn $wrapper_name(seed: *mut noise::Seed, x: $t, y: $t, z: $t, w: $t) -> $t {
            let seed: &noise::Seed = &*seed;
            $alg(&seed, &[x, y, z, w])
        }
    }
}

#2

Here is one approach:

macro_rules! wrap_noise {
    ($wrapper_name:ident, $t:ty, $alg:expr, 0) => {
        wrap_noise!($wrapper_name, $t, $alg);
    };
    ($wrapper_name:ident, $t:ty, $alg:expr, 1) => {
        wrap_noise!($wrapper_name, $t, $alg, x);
    };
    ($wrapper_name:ident, $t:ty, $alg:expr, 2) => {
        wrap_noise!($wrapper_name, $t, $alg, x, y);
    };
    ($wrapper_name:ident, $t:ty, $alg:expr, 3) => {
        wrap_noise!($wrapper_name, $t, $alg, x, y, z);
    };
    ($wrapper_name:ident, $t:ty, $alg:expr $(, $arg:ident)*) => {
        #[no_mangle]
        pub unsafe fn $wrapper_name(seed: *mut noise::Seed $(, $arg: $t)*) -> $t {
            let seed: &noise::Seed = &*seed;
            $alg(&seed, &[$($arg),*])
        }
    };
}

#3

That’s great! Thanks!