Skip to content

Commit f2dcd27

Browse files
committed
Introduce a op module with struct representations of each routine
This contains: 1. Per-function and per-operation enums created by the proc macro 2. The `MathOp` trait which is implemented once per struct representing a function 3. Submodules for each function, each containing a `Routine` struct that implements `MathOp`
1 parent 06809a2 commit f2dcd27

File tree

3 files changed

+115
-2
lines changed

3 files changed

+115
-2
lines changed

crates/libm-test/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
pub mod gen;
22
#[cfg(feature = "test-multiprecision")]
33
pub mod mpfloat;
4+
pub mod op;
45
mod precision;
56
mod test_traits;
67

78
pub use libm::support::{Float, Int};
9+
pub use op::{BaseName, MathOp, Name};
810
pub use precision::{MaybeOverride, SpecialCase, multiprec_allowed_ulp, musl_allowed_ulp};
911
pub use test_traits::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, Hex, TupleCall};
1012

crates/libm-test/src/op.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//! Types representing individual functions.
2+
//!
3+
//! Each routine gets a module with its name, e.g. `mod sinf { /* ... */ }`. The module
4+
//! contains a unit struct `Routine` which implements `MathOp`.
5+
//!
6+
//! Basically everything could be called a "function" here, so we loosely use the following
7+
//! terminology:
8+
//!
9+
//! - "Function": the math operation that does not have an associated precision. E.g. `f(x) = e^x`,
10+
//! `f(x) = log(x)`.
11+
//! - "Routine": A code implementation of a math operation with a specific precision. E.g. `exp`,
12+
//! `expf`, `expl`, `log`, `logf`.
13+
//! - "Operation" / "Op": Something that relates a routine to a function or is otherwise higher
14+
//! level. `Op` is also used as the name for generic parameters since it is terse.
15+
16+
use crate::{CheckOutput, Float, TupleCall};
17+
18+
/// An enum representing each possible routine name.
19+
#[libm_macros::function_enum(BaseName)]
20+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21+
pub enum Name {}
22+
23+
/// The name without any type specifier, e.g. `sin` and `sinf` both become `sin`.
24+
#[libm_macros::base_name_enum]
25+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26+
pub enum BaseName {}
27+
28+
/// Attributes ascribed to a `libm` routine including signature, type information,
29+
/// and naming.
30+
pub trait MathOp {
31+
/// The float type used for this operation.
32+
type FTy: Float;
33+
34+
/// The function type representing the signature in a C library.
35+
type CFn: Copy;
36+
37+
/// Arguments passed to the C library function as a tuple. These may include `&mut` return
38+
/// values.
39+
type CArgs<'a>
40+
where
41+
Self: 'a;
42+
43+
/// The type returned by C implementations.
44+
type CRet;
45+
46+
/// The signature of the Rust function as a `fn(...) -> ...` type.
47+
type RustFn: Copy;
48+
49+
/// Arguments passed to the Rust library function as a tuple.
50+
///
51+
/// The required `TupleCall` bounds ensure this type can be passed either to the C function or
52+
/// to the Rust function.
53+
type RustArgs: Copy
54+
+ TupleCall<Self::RustFn, Output = Self::RustRet>
55+
+ TupleCall<Self::CFn, Output = Self::RustRet>;
56+
57+
/// Type returned from the Rust function.
58+
type RustRet: CheckOutput<Self::RustArgs>;
59+
60+
/// The name of this function, including suffix (e.g. `sin`, `sinf`).
61+
const NAME: Name;
62+
63+
/// The name as a string.
64+
const NAME_STR: &'static str = Self::NAME.as_str();
65+
66+
/// The name of the function excluding the type suffix, e.g. `sin` and `sinf` are both `sin`.
67+
const BASE_NAME: BaseName = Self::NAME.base_name();
68+
69+
/// The function in `libm` which can be called.
70+
const ROUTINE: Self::RustFn;
71+
}
72+
73+
macro_rules! do_thing {
74+
// Matcher for unary functions
75+
(
76+
fn_name: $fn_name:ident,
77+
FTy: $FTy:ty,
78+
CFn: $CFn:ty,
79+
CArgs: $CArgs:ty,
80+
CRet: $CRet:ty,
81+
RustFn: $RustFn:ty,
82+
RustArgs: $RustArgs:ty,
83+
RustRet: $RustRet:ty,
84+
) => {
85+
paste::paste! {
86+
pub mod $fn_name {
87+
use super::*;
88+
pub struct Routine;
89+
90+
impl MathOp for Routine {
91+
type FTy = $FTy;
92+
type CFn = for<'a> $CFn;
93+
type CArgs<'a> = $CArgs where Self: 'a;
94+
type CRet = $CRet;
95+
type RustFn = $RustFn;
96+
type RustArgs = $RustArgs;
97+
type RustRet = $RustRet;
98+
99+
const NAME: Name = Name::[< $fn_name:camel >];
100+
const ROUTINE: Self::RustFn = libm::$fn_name;
101+
}
102+
}
103+
104+
}
105+
};
106+
}
107+
108+
libm_macros::for_each_function! {
109+
callback: do_thing,
110+
emit_types: all,
111+
}

crates/libm-test/src/test_traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ where
137137
}
138138
}
139139

140-
impl<T1, T2, T3> TupleCall<fn(T1, &mut T2, &mut T3)> for (T1,)
140+
impl<T1, T2, T3> TupleCall<for<'a> fn(T1, &'a mut T2, &'a mut T3)> for (T1,)
141141
where
142142
T1: fmt::Debug,
143143
T2: fmt::Debug + Default,
144144
T3: fmt::Debug + Default,
145145
{
146146
type Output = (T2, T3);
147147

148-
fn call(self, f: fn(T1, &mut T2, &mut T3)) -> Self::Output {
148+
fn call(self, f: for<'a> fn(T1, &'a mut T2, &'a mut T3)) -> Self::Output {
149149
let mut t2 = T2::default();
150150
let mut t3 = T3::default();
151151
f(self.0, &mut t2, &mut t3);

0 commit comments

Comments
 (0)