-
Notifications
You must be signed in to change notification settings - Fork 1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Discussion: Permit static lambda assignment to function pointers #3476
Comments
This requires a new feature which amends these parts of the 'static lambdas' speclet:
We will now need to guarantee that a 'static' lambda is emitted as 'static' if its address is taken.
Removing the 'static' modifier in a context where it is converted to function pointer will introduce a compile-time error. |
Presuming, of course, that we would want to make those changes. We explicitly did not make any guarantees about how |
To me, this feels like a feature gap in the language, something that you would expect to be possible but it doesn't work merely because it's not implemented. But I'd agree that it's not very common because function pointers hasn't been shipped yet ;) |
That's not how I think about this at all. Function pointers aren't a common use case. They require unsafe, and getting a pointer requires taking the address of a static method. I wouldn't expect them to work with anything else. |
Within that context, one could decide to use a function pointer instead of a delegate for performance reasons. Then they need to use a local function anywhere they previously used a (static) lambda. Now we're back where we felt the need for static lambdas in the first place (while static local functions were available). |
I understand that technically a proper method is required to acquire an address, but this would be a convenient syntactic sugar much like what has been done for |
Tbh why not just make the static lambda a private static member in the type then convert that to a function pointer and return it where you need to use it instead of using a |
Wanted to address #6111 (comment) in the original issue (cc @HaloFour)
Func<int, int> del = static int (int x) => x;
delegate*<int, int> funcPtr = &static int (int x) => x; A lambda is only used once, so it seems reasonable that the way it is used could affect the way it is lowered. Why couldn't the compiler emit a non- |
I'd like to see more of that in general, like if a lambda is only invoked from within the method in which it is declared the compiler could emit it as a local function instead, save an allocation or two. |
I'm probably missing something but how it could possibly save any allocations? |
I think @HaloFour's suggestion was to adjust lowering/emit in some cases so that no local variable is introduced and no delegate is actually allocated for the lambda. Func<int, int> multiply = x => x * 2;
Console.Write(multiply(2));
// equivalent to
int multiply(int x) => x * 2;
Console.Write(multiply(2)); |
Got it. I thought it has something to do with |
Considering this spec
I would find it useful to be able to directly assign a lambda to a function pointer without My use case is, I have a library that supports old versions of Unity (C# 4), and I conditionally compile with newer C# language features for better performance. A simplified example similar to what I'm doing: internal delegate void ConvertDelegate<T>(object asyncResult, ref T storage, int index);
internal Merger<T> : IAsyncResult<T>, IIndexedCallback
{
internal T _result;
private ConvertDelegate<T> _converter;
internal Merger(ConvertDelegate<T> converter)
{
_converter = converter;
}
void IIndexedCallback.Callback(object asyncResult, int index)
{
_result = _converter(asyncResult, ref _result, index);
}
T IAsyncResult<T>.Result { get { return _result; } }
}
public static AsyncResult<ValueTuple<T1, T2>> Merge<T1, T2>(AsyncResult<T1> result1, AsyncResult<T2> result2)
{
var merger = new Merger<ValueTuple<T1, T2>>((object asyncResult, ref ValueTuple<T1, T2> storage, int index) =>
{
if (index == 0)
{
storage.Item1 = ((IAsyncResult<T1>) asyncResult).Result;
}
else
{
storage.Item2 = ((IAsyncResult<T2>) asyncResult).Result;
}
});
result1.AddIndexedListener(merger, 0);
result2.AddIndexedListener(merger, 1);
return new AsyncResult<ValueTuple<T1, T2>>(merger);
}
...
public static AsyncResult<ValueTuple<T1, T2, T3, T4>> Merge<T1, T2, T3, T4>(AsyncResult<T1> result1, AsyncResult<T2> result2, AsyncResult<T3> result3, AsyncResult<T4> result4)
{
...
} And it would be very convenient for me if I could just change the #if CSHARP_12_OR_GREATER // Or whichever language version
private delegate*<object, ref T, int, void> _converter;
internal Merger(delegate*<object, ref T, int, void> converter)
{
_converter = converter;
}
#else
private ConvertDelegate<T> _converter;
internal Merger(ConvertDelegate<T> converter)
{
_converter = converter;
}
#endif I know my use case is super niche and this may be unlikely to happen, but I figured I should give my input here. |
Being able to pass static lambdas to function pointers would be nice, but I must warn you, older Unity versions don't always handle |
That's why I use conditional compilation for older Unity versions to just use the regular delegate. |
delegate* unmanaged[Cdecl]<int, float> d = &static (i) => (float)i; This could also implicitly use UnmanagedCallersOnly when encountering an assignment to an unmanaged pointer, would be pretty useful with interop scenarios now that I think about it. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
It should be safe to pass a static lambda to a function pointer.
Otherwise you must fallback to using local function and declare everything explicitly.
The text was updated successfully, but these errors were encountered: