Exemplo n.º 1
0
 // Notes:
 //  - This class does not support integer types smaller than 'int' because the function
 //    signatures for 'Plus', 'Neg' are not the same as for types >= 'int' (i.e. +(byte)1 returns an int)
 //  - If you get 'InvalidOperationException's in here, it's because instantiating 'Operators<YourType>' is
 //    not possible for one or more of the operators. You'll need to specialise to handle the type.
 //  - Try to write these as: if (positive_logic) {} else if (positive_logic) {} else { failure function }
 static Operators()
 {
     #region MaxValue
     {
         var mi = typeof(T).GetProperty("MaxValue", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetGetMethod();
         var fi = typeof(T).GetField("MaxValue", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
         if (mi != null)
         {
             m_max_value = () => (T)mi.Invoke(null, null) !;
         }
         else if (fi != null)
         {
             m_max_value = () => (T)fi.GetValue(null) !;
         }
         else
         {
             m_max_value = () => { throw new Exception($"Type {typeof(T).Name} has no static property or field named 'MaxValue'"); };
         }
     }
     #endregion
     #region MinValue
     {
         var mi = typeof(T).GetProperty("MinValue", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetGetMethod();
         var fi = typeof(T).GetField("MinValue", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
         if (mi != null)
         {
             m_min_value = () => (T)mi.Invoke(null, null) !;
         }
         else if (fi != null)
         {
             m_min_value = () => (T)fi.GetValue(null) !;
         }
         else
         {
             m_min_value = () => { throw new Exception($"Type {typeof(T).Name} has no static property or field named 'MinValue'"); };
         }
     }
     #endregion
     #region Plus
     {
         if (!typeof(T).IsEnum)
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var body   = Expression.UnaryPlus(paramA);
             m_plus = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else
         {
             m_plus = x => { throw new Exception($"Type {typeof(T).Name} does not define the Unary Plus operator"); };
         }
     }
     #endregion
     #region Neg
     {
         // Unsigned types don't define unary minus. Following the MS convention of -a == 2^N - a, where N is the number of bits
         if (typeof(T) == typeof(uint))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var body   = Expression.Subtract(Expression.Constant(0U), paramA);
             m_neg = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else if (typeof(T) == typeof(ulong))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var body   = Expression.Subtract(Expression.Constant(0UL), paramA);
             m_neg = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else if (!typeof(T).IsEnum)
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var body   = Expression.Negate(paramA);
             m_neg = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else
         {
             m_neg = x => { throw new Exception($"Type {typeof(T).Name} does not define the Unary Minus operator"); };
         }
     }
     #endregion
     #region Ones complement
     {
         if (typeof(T).IsEnum)
         {
             if (typeof(T).HasAttribute <FlagsAttribute>())
             {
                 var paramA = Expression.Parameter(typeof(T), "a");
                 var cast0  = Expression.Convert(paramA, Enum.GetUnderlyingType(typeof(T)));
                 var comp   = Expression.OnesComplement(cast0);
                 var body   = Expression.Convert(comp, typeof(T));
                 m_ones_comp = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
             }
             else
             {
                 m_ones_comp = x => { throw new Exception($"Type {typeof(T).Name} does not define the Ones Complement operator"); };
             }
         }
         else if (typeof(T).IsPrimitive && typeof(T) != typeof(float) && typeof(T) != typeof(double) && typeof(T) != typeof(decimal))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var body   = Expression.OnesComplement(paramA);
             m_ones_comp = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else
         {
             m_ones_comp = x => { throw new Exception($"Type {typeof(T).Name} does not define the Ones Complement operator"); };
         }
     }
     #endregion
     #region Add
     {
         if (!typeof(T).IsEnum)
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.Add(paramA, paramB);
             m_add = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_add = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Add operator"); };
         }
     }
     #endregion
     #region Sub
     {
         if (!typeof(T).IsEnum)
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.Subtract(paramA, paramB);
             m_sub = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_sub = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Subtraction operator"); };
         }
     }
     #endregion
     #region Multiply
     {
         if (!typeof(T).IsEnum &&
             typeof(T) != typeof(TimeSpan))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.Multiply(paramA, paramB);
             m_mul = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_mul = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Multiplication operator"); };
         }
     }
     #endregion
     #region Divide
     {
         if (!typeof(T).IsEnum &&
             typeof(T) != typeof(TimeSpan) &&
             !Math_.IsVecMatType(typeof(T)))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.Divide(paramA, paramB);
             m_div = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_div = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Division operator"); };
         }
     }
     #endregion
     #region Sqrt
     {
         if (typeof(T).IsPrimitive || typeof(T) == typeof(decimal))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var sqrt   = typeof(Math).GetMethod(nameof(Math.Sqrt), new[] { typeof(double) });
             var body   = Expression.ConvertChecked(Expression.Call(sqrt, Expression.ConvertChecked(paramA, typeof(double))), typeof(T));
             m_sqrt = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else if (typeof(T) == typeof(v4))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var sqrt   = typeof(Math_).GetMethod(nameof(Math_.Sqrt), new[] { typeof(T) });
             var body   = Expression.ConvertChecked(Expression.Call(sqrt, paramA), typeof(T));
             m_sqrt = Expression.Lambda <Func <T, T> >(body, paramA).Compile();
         }
         else
         {
             m_sqrt = (a) => { throw new Exception($"Type {typeof(T).Name} does not define the Sqrt operator"); };
         }
     }
     #endregion
     #region Bitwise OR
     {
         if (typeof(T).IsEnum)
         {
             if (typeof(T).HasAttribute <FlagsAttribute>())
             {
                 var paramA = Expression.Parameter(typeof(T), "a");
                 var paramB = Expression.Parameter(typeof(T), "a");
                 var castA0 = Expression.Convert(paramA, Enum.GetUnderlyingType(typeof(T)));
                 var castB0 = Expression.Convert(paramB, Enum.GetUnderlyingType(typeof(T)));
                 var or     = Expression.Or(castA0, castB0);
                 var body   = Expression.Convert(or, typeof(T));
                 m_bitwise_or = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
             }
             else
             {
                 m_bitwise_or = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Bitwise OR operator"); };
             }
         }
         else if (typeof(T).IsPrimitive && typeof(T) != typeof(float) && typeof(T) != typeof(double) && typeof(T) != typeof(decimal))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.Or(paramA, paramB);
             m_bitwise_or = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_bitwise_or = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Bitwise OR operator"); };
         }
     }
     #endregion
     #region Bitwise AND
     {
         if (typeof(T).IsEnum)
         {
             if (typeof(T).HasAttribute <FlagsAttribute>())
             {
                 var paramA = Expression.Parameter(typeof(T), "a");
                 var paramB = Expression.Parameter(typeof(T), "a");
                 var castA0 = Expression.Convert(paramA, Enum.GetUnderlyingType(typeof(T)));
                 var castB0 = Expression.Convert(paramB, Enum.GetUnderlyingType(typeof(T)));
                 var and    = Expression.And(castA0, castB0);
                 var body   = Expression.Convert(and, typeof(T));
                 m_bitwise_and = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
             }
             else
             {
                 m_bitwise_and = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Bitwise AND operator"); };
             }
         }
         else if (typeof(T).IsPrimitive && typeof(T) != typeof(float) && typeof(T) != typeof(double) && typeof(T) != typeof(decimal))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.And(paramA, paramB);
             m_bitwise_and = Expression.Lambda <Func <T, T, T> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_bitwise_and = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the Bitwise AND operator"); };
         }
     }
     #endregion
     #region Equal
     {
         var paramA = Expression.Parameter(typeof(T), "a");
         var paramB = Expression.Parameter(typeof(T), "b");
         var body   = Expression.Equal(paramA, paramB);
         m_eql = Expression.Lambda <Func <T, T, bool> >(body, paramA, paramB).Compile();
     }
     #endregion
     #region Less Than
     {
         if (typeof(T).IsEnum)
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var castA0 = Expression.Convert(paramA, Enum.GetUnderlyingType(typeof(T)));
             var castB0 = Expression.Convert(paramB, Enum.GetUnderlyingType(typeof(T)));
             var body   = Expression.LessThan(castA0, castB0);
             m_less = Expression.Lambda <Func <T, T, bool> >(body, paramA, paramB).Compile();
         }
         else if (!Math_.IsVecMatType(typeof(T)))
         {
             var paramA = Expression.Parameter(typeof(T), "a");
             var paramB = Expression.Parameter(typeof(T), "b");
             var body   = Expression.LessThan(paramA, paramB);
             m_less = Expression.Lambda <Func <T, T, bool> >(body, paramA, paramB).Compile();
         }
         else
         {
             m_less = (a, b) => { throw new Exception($"Type {typeof(T).Name} does not define the LessThan operator"); };
         }
     }
     #endregion
     #region ToString
     {
         var mi1 = typeof(T).GetMethod("ToString", new[] { typeof(IFormatProvider) });
         var mi2 = typeof(T).GetMethod("ToString", new[] { typeof(string) });
         var mi3 = typeof(T).GetMethod("ToString", new[] { typeof(string), typeof(IFormatProvider) });
         if (mi1 != null)
         {
             m_tostring1 = (a, fp) => (string?)mi1.Invoke(a, new object[] { fp });
         }
         else
         {
             m_tostring1 = (a, fp) => { throw new Exception($"Type {typeof(T).Name} has no ToString(IFormatProvider) overload"); }
         };
         if (mi2 != null)
         {
             m_tostring2 = (a, fmt) => (string?)mi2.Invoke(a, new object[] { fmt });
         }
         else
         {
             m_tostring2 = (a, fmt) => { throw new Exception($"Type {typeof(T).Name} has no ToString(string) overload"); }
         };
         if (mi3 != null)
         {
             m_tostring3 = (a, fmt, fp) => (string?)mi3.Invoke(a, new object[] { fmt, fp });
         }
         else
         {
             m_tostring3 = (a, fmt, fp) => { throw new Exception($"Type {typeof(T).Name} has no ToString(string, IFormatProvider) overload"); }
         };
     }
     #endregion
     #region Parse
     {
         var mi = typeof(T).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, new Type[] { typeof(string) }, null);
         if (mi != null)
         {
             m_parse = s => (T)mi.Invoke(null, new object[] { s }) !;
         }
         else
         {
             m_parse = s => { throw new Exception($"Type {typeof(T).Name} has no static method named 'Parse'"); };
         }
     }
     #endregion
 }