1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//
// macros_ops.rs
// Copyright (C) 2019 Malcolm Ramsay <malramsay64@gmail.com>
// Distributed under terms of the MIT license.
//
//

#![macro_use]

/// Implement a binary operation
///
/// This converts a set of values and types to an impl block for a specific operation. In doing
/// this it is possible to implement all the operations at once with the binop_impl_all macro.
///
macro_rules! _binop_impl(
    ($Op: ident, $op: ident;
     $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty;
     $action: expr; $($lives: tt),*) => {
        impl<$($lives ,)*> $Op<$Rhs> for $Lhs {
            type Output = $Output;

            #[inline]
            fn $op($lhs, $rhs: $Rhs) -> Self::Output {
                $action
            }
        }
    }
);

/// Implement all combinations of operations
///
/// When implementing an operation there are four different operations that need to be
/// implemented to cover all use cases, since an operation of a reference is considered different
/// to on a value. This is a macro which makes writing the implementation of that operation much
/// simpler, only needing to write out the implementation for the reference * reference values.
///
/// This macro is adapted from the
/// [nalgebra](https://github.com/rustsim/nalgebra/blob/911ddca588d2c89e4f7ec9b45aa0aa7f787209c4/src/geometry/isometry_ops.rs)
/// source code.
///
/// # Example
///
/// ```
/// # use crate::ops_macros::binop_impl_all;
/// struct Wrapper(f64);
///
/// binop_impl_all!(
///     Mul, mul;
///     self: Wrapper, rhs: f64, Output = f64;
///     [ref ref] => self * rhs
/// )
///
///
/// let w = Wrapper(8.);
/// w * 2.
/// # assert!(w * 2. == 16.)
///
/// ```
macro_rules! binop_impl_all(
    // The trait we implementing, along with the function required to implement the trait
    ($Op: ident, $op: ident;
     // This is types and variable names of each side of the operand, and then the type of the
     // output.
     $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty;
     // These are the expressions used to perform each of the operations on the different
     // combinations of value and reference. When implementing, by converting all values to the
     // form ref * ref, there is only a single implementation required.
     [ref ref] => $action_ref_ref: expr;) => {
        // This is the block where the transformation actually occurs.
        _binop_impl!(
            $Op, $op;
            $lhs: $Lhs, $rhs: $Rhs, Output = $Output;
            &$lhs * &$rhs; );

        _binop_impl!(
            $Op, $op;
            $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output;
            $lhs * &$rhs; 'a);

        _binop_impl!(
            $Op, $op;
            $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output;
            &$lhs * $rhs; 'b);

        _binop_impl!(
            $Op, $op;
            $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output;
            $action_ref_ref; 'a, 'b);
    }
);