-
Notifications
You must be signed in to change notification settings - Fork 387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating compiled lambda expression math class for future use #698
Conversation
Codecov Report
@@ Coverage Diff @@
## master #698 +/- ##
==========================================
+ Coverage 58.34% 59.01% +0.66%
==========================================
Files 165 172 +7
Lines 37450 39616 +2166
==========================================
+ Hits 21852 23379 +1527
- Misses 15598 16237 +639
Continue to review full report at Codecov.
|
Some types may not support all the operators. You don't want to build all the operations at once. That means if I call "Add" but the type doesn't support "GreaterThan" it would throw an exception in the static constructor. Also, you did not add the logic operators to your code (I added them in my example here). You should alter the code to something like this: internal static class RuntmeCompiledFunctions
{
internal static class Multiply<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function = CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Multiply);
}
internal static class Divide<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function = CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Divide);
}
internal static class Add<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function = CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Add);
}
internal static class Subtract<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function = CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Subtract);
}
internal static class Equal<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.Equal);
}
internal static class NotEqual<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.NotEqual);
}
internal static class LessThan<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.LessThan);
}
internal static class GreaterThan<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.GreaterThan);
}
internal static class GreaterThanOrEqual<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.GreaterThanOrEqual);
}
internal static class LessThanOrEqual<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function = CreateBinaryFunction<TLeft, TRight, bool>(Expression.LessThanOrEqual);
}
internal static class Modulo<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function = CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Modulo);
}
private static Func<TLeft, TRight, TResult> CreateBinaryFunction<TLeft, TRight, TResult>(Func<Expression, Expression, BinaryExpression> expressionCreationFunction)
{
var leftParameter = Expression.Parameter(typeof(TLeft), "left");
var rightParameter = Expression.Parameter(typeof(TRight), "right");
var binaryExpression = expressionCreationFunction(leftParameter, rightParameter);
var lambda = Expression.Lambda<Func<TLeft, TRight, TResult>>(binaryExpression, leftParameter, rightParameter);
return lambda.Compile();
}
} |
@ZacharyPatten I agree although it ends up a bit long-winded constantly calling Calling via internal static class CompiledLambdas
{
internal static TResult Multiply<TLeft, TRight, TResult>(TLeft left, TRight right) =>
MultiplyHelper<TLeft, TRight, TResult>.Function(left, right);
internal static TResult Divide<TLeft, TRight, TResult>(TLeft left, TRight right) =>
DivideHelper<TLeft, TRight, TResult>.Function(left, right);
internal static TResult Add<TLeft, TRight, TResult>(TLeft left, TRight right) =>
AddHelper<TLeft, TRight, TResult>.Function(left, right);
internal static TResult Subtract<TLeft, TRight, TResult>(TLeft left, TRight right) =>
SubtractHelper<TLeft, TRight, TResult>.Function(left, right);
internal static bool Equal<TLeft, TRight>(TLeft left, TRight right) =>
EqualHelper<TLeft, TRight>.Function(left, right);
internal static bool NotEqual<TLeft, TRight, TResult>(TLeft left, TRight right) =>
NotEqualHelper<TLeft, TRight>.Function(left, right);
internal static bool LessThan<TLeft, TRight, TResult>(TLeft left, TRight right) =>
LessThanHelper<TLeft, TRight>.Function(left, right);
internal static bool GreaterThan<TLeft, TRight, TResult>(TLeft left, TRight right) =>
GreaterThanHelper<TLeft, TRight>.Function(left, right);
internal static bool GreaterThanOrEqual<TLeft, TRight, TResult>(TLeft left, TRight right) =>
GreaterThanOrEqualHelper<TLeft, TRight>.Function(left, right);
internal static bool LessThanOrEqual<TLeft, TRight, TResult>(TLeft left, TRight right) =>
LessThanOrEqualHelper<TLeft, TRight>.Function(left, right);
internal static TResult Modulo<TLeft, TRight, TResult>(TLeft left, TRight right) =>
ModuloHelper<TLeft, TRight, TResult>.Function(left, right);
#region Helper Classes
private static class MultiplyHelper<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function =
CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Multiply);
}
private static class DivideHelper<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function =
CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Divide);
}
private static class AddHelper<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function =
CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Add);
}
private static class SubtractHelper<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function =
CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Subtract);
}
private static class EqualHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.Equal);
}
private static class NotEqualHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.NotEqual);
}
private static class LessThanHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.LessThan);
}
private static class GreaterThanHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.GreaterThan);
}
private static class GreaterThanOrEqualHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.GreaterThanOrEqual);
}
private static class LessThanOrEqualHelper<TLeft, TRight>
{
internal readonly static Func<TLeft, TRight, bool> Function =
CreateBinaryFunction<TLeft, TRight, bool>(Expression.LessThanOrEqual);
}
private static class ModuloHelper<TLeft, TRight, TResult>
{
internal readonly static Func<TLeft, TRight, TResult> Function =
CreateBinaryFunction<TLeft, TRight, TResult>(Expression.Modulo);
}
#endregion
private static Func<TLeft, TRight, TResult> CreateBinaryFunction<TLeft, TRight, TResult>(Func<Expression, Expression, BinaryExpression> expressionCreationFunction)
{
var leftParameter = Expression.Parameter(typeof(TLeft), "left");
var rightParameter = Expression.Parameter(typeof(TRight), "right");
var binaryExpression = expressionCreationFunction(leftParameter, rightParameter);
var lambda = Expression.Lambda<Func<TLeft, TRight, TResult>>(binaryExpression, leftParameter, rightParameter);
return lambda.Compile();
}
} You could even add this so you can deduce the parameters and just call internal static T Add<T>(T left, T right) =>
AddHelper<T, T, T>.Function(left, right); |
The C# compiler can infer generic parameters on methods. If you want to simplify the syntax, you can wrap the calls in methods and provide overloads in which the generic parameters can be inferred by the compiler. That is what I did in my Compute class here: https://github.com/ZacharyPatten/Towel/blob/master/Sources/Towel/Mathematics/Compute.cs You can call it like so: Compute.Add(1, 2);
Compute.Add(1.5f, 2.5f); The compiler will never be able to infer generic parameters that are used for the return type though. Edit: After re-reading your comment I realized you already mentioned allowing the compile to infertypes. Yes, that is a very good approach. |
@ZacharyPatten yup - just looked at your example and it's exactly what I did in the above example. I'll update the code. |
I've only glanced through this thread so far, let me know when you want a review @tmilnthorp . |
I think it's ready. Just not used yet :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some minor things, this looks awesome.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Bad bot, reopening this one. |
Codecov Report
@@ Coverage Diff @@
## master #698 +/- ##
==========================================
+ Coverage 63.87% 63.90% +0.02%
==========================================
Files 279 280 +1
Lines 41644 41691 +47
==========================================
+ Hits 26601 26641 +40
- Misses 15043 15050 +7
Continue to review full report at Codecov.
|
I've forgotten a lot of the details here, but it seems I was happy the last time I reviewed it and you have now addressed my comment, so.. I'll merge this then :-) |
For potential future use for generic quantity structs