blob: b4276262229fe4ef64699715c03af65aa0e189a5 [file] [log] [blame]
#![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}");
}
}