// 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 }