| #![feature(f16)] |
| #![feature(f128)] |
| // `STATUS_DLL_NOT_FOUND` on i686 MinGW, not worth looking into. |
| #![cfg(not(all(target_arch = "x86", target_os = "windows", target_env = "gnu")))] |
| |
| macro_rules! basic { |
| ( |
| fn_name: $fn_name:ident, |
| FTy: $FTy:ty, |
| CFn: $CFn:ty, |
| CArgs: $CArgs:ty, |
| CRet: $CRet:ty, |
| RustFn: $RustFn:ty, |
| RustArgs: $RustArgs:ty, |
| RustRet: $RustRet:ty, |
| public: $public:expr, |
| attrs: [$($attr:meta),*], |
| extra: [$($extra_tt:tt)*], |
| fn_extra: $fn_extra:expr, |
| ) => { |
| $(#[$attr])* |
| #[allow(dead_code)] |
| pub mod $fn_name { |
| type FTy= $FTy; |
| type CFnTy<'a> = $CFn; |
| type RustFnTy = $RustFn; |
| type RustArgsTy = $RustArgs; |
| type RustRetTy = $RustRet; |
| const PUBLIC: bool = $public; |
| const A: &[&str] = &[$($extra_tt)*]; |
| fn foo(a: f32) -> f32 { |
| $fn_extra(a) |
| } |
| } |
| }; |
| } |
| |
| mod test_basic { |
| libm_macros::for_each_function! { |
| callback: basic, |
| emit_types: all, |
| skip: [sin, cos], |
| attributes: [ |
| // just some random attributes |
| #[allow(clippy::pedantic)] |
| #[allow(dead_code)] |
| [sinf, cosf] |
| ], |
| extra: ["foo", "bar"], |
| fn_extra: match MACRO_FN_NAME { |
| sin => |x| x + 2.0, |
| cos | cosf => |x: f32| x.MACRO_FN_NAME_NORMALIZED(), |
| _ => |_x| 100.0 |
| } |
| } |
| } |
| |
| macro_rules! basic_no_extra { |
| ( |
| fn_name: $fn_name:ident, |
| attrs: [$($attr:meta),*], |
| ) => { |
| $(#[$attr])* |
| mod $fn_name {} |
| }; |
| } |
| |
| mod test_basic_no_extra { |
| // Test with no extra, no skip, and no attributes |
| libm_macros::for_each_function! { |
| callback: basic_no_extra, |
| } |
| } |
| |
| mod test_only { |
| // Test that only works |
| libm_macros::for_each_function! { |
| callback: basic_no_extra, |
| only: [sin, sinf], |
| } |
| } |
| |
| macro_rules! specified_types { |
| ( |
| fn_name: $fn_name:ident, |
| RustFn: $RustFn:ty, |
| RustArgs: $RustArgs:ty, |
| attrs: [$($attr:meta),*], |
| ) => { |
| $(#[$attr])* |
| #[allow(dead_code)] |
| mod $fn_name { |
| type RustFnTy = $RustFn; |
| type RustArgsTy = $RustArgs; |
| } |
| }; |
| } |
| |
| mod test_emit_types { |
| // Test that we can specify a couple types to emit |
| libm_macros::for_each_function! { |
| callback: specified_types, |
| emit_types: [RustFn, RustArgs], |
| } |
| } |
| |
| #[test] |
| fn test_skip_f16_f128() { |
| macro_rules! skip_f16_f128 { |
| ( |
| fn_name: $fn_name:ident, |
| attrs: [$($attr:meta),*], |
| extra: $vec:ident, |
| ) => { |
| $vec.push(stringify!($fn_name)); |
| }; |
| } |
| |
| let mut v = Vec::new(); |
| // Test with no extra, no skip, and no attributes |
| libm_macros::for_each_function! { |
| callback: skip_f16_f128, |
| skip_f16_f128: true, |
| extra: v, |
| } |
| |
| for name in v { |
| assert!(!name.contains("f16"), "{name}"); |
| assert!(!name.contains("f128"), "{name}"); |
| } |
| } |
| |
| #[test] |
| fn test_fn_extra_expansion() { |
| macro_rules! fn_extra_expansion { |
| ( |
| fn_name: $fn_name:ident, |
| attrs: [$($attr:meta),*], |
| fn_extra: $vec:expr, |
| ) => { |
| $vec.push(stringify!($fn_name)); |
| }; |
| } |
| |
| let mut vf16 = Vec::new(); |
| let mut vf32 = Vec::new(); |
| let mut vf64 = Vec::new(); |
| let mut vf128 = Vec::new(); |
| |
| // Test with no extra, no skip, and no attributes |
| libm_macros::for_each_function! { |
| callback: fn_extra_expansion, |
| fn_extra: match MACRO_FN_NAME { |
| ALL_F16 => vf16, |
| ALL_F32 => vf32, |
| ALL_F64 => vf64, |
| ALL_F128 => vf128, |
| } |
| } |
| |
| // Skip functions with a suffix after the type spec |
| vf16.retain(|name| !name.ends_with("_r")); |
| vf32.retain(|name| !name.ends_with("_r")); |
| vf64.retain(|name| !name.ends_with("_r")); |
| vf128.retain(|name| !name.ends_with("_r")); |
| |
| for name in vf16 { |
| assert!(name.ends_with("f16"), "{name}"); |
| } |
| for name in vf32 { |
| assert!(name.ends_with("f"), "{name}"); |
| } |
| let _ = vf64; |
| for name in vf128 { |
| assert!(name.ends_with("f128"), "{name}"); |
| } |
| } |