-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Consider allow pointers as generic type arguments #13627
Comments
I can see |
I would imagine it works the same as Edit: Noting that this is the same codegen as is already done for an arbitrary |
I meant something more like this |
That would likely require some work (either on the language side or the runtime side) to ensure things don't blow up 😄. I imagine there will be a few places like that if the relaxation was allowed. |
The main restriction is that generic parameters can be boxed (one can do Making pointers derive from I expect there would be a pretty long bug tail after that. |
Having a special-cased |
IMHO this issue belongs to https://github.com/dotnet/csharplang/ repo. Otherwise, I fully support the idea. It's a huge simplification for multiple coding patterns. |
The C# language can't reliably do this without the restriction first being lifted in the runtime. Now it's also reasonable that the runtime wouldn't do this without the language also agreeing to expose said support, but ensuring it is feasible for the runtime to handle is the first step (thus this issue and the note "If that is possible, I would open a corresponding proposal on dotnet/csharplang suggesting that the language allow the same."). |
IMHO this is a C# language feature which requires runtime support. Reading between lines I understand you want to avoid pushback in csharplang repo due to the requirement of the runtime support. But this doesn't change the fact that the issue should go to dotnet/csharplang . |
Assuming that this work would enable function pointers as type arguments as well, then FWIW, I have a function hooking API in one of my projects that would benefit considerably from this. (Apologies in advance for the somewhat lengthy comment.) The API shape currently looks like this (simplified): public sealed unsafe class FunctionHook
{
public void* Target { get; }
public void* Hook { get; }
public void* Trampoline { get; }
public object? State { get; }
public static FunctionHook Current { get; }
public static FunctionHook Create(void* target, void* hook, object? state = null);
} Usage looks like this: [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
static int SetThreadDescriptionHook(nint hThread, char* lpThreadDescription)
{
Console.WriteLine(new string(lpThreadDescription) + FunctionHook.Current.State);
return 0;
}
var kernel32 = NativeLibrary.Load("kernel32.dll");
var setThreadDescription = (delegate* unmanaged[Stdcall]<nint, char*, int>)NativeLibrary.GetExport("SetThreadDescription");
using (var hook = FunctionHook.Create(func, (delegate* unmanaged[Stdcall]<nint, char*, int>)&SetThreadDescriptionHook, "bar"))
fixed (char* p = "foo")
_ = setThreadDescription(-1, p); // Prints "foobar". I would much rather have an API shape like this: public sealed unsafe class FunctionHook<T>
where T : delegate* /* or simply : unmanaged */
{
public T Target { get; }
public T Hook { get; }
public T Trampoline { get; }
public object? State { get; }
public static FunctionHook Current { get; }
}
public sealed unsafe class FunctionHook
{
// (target is typed as void* since you almost always obtain it as that and forcing an extra cast doesn't actually add value.)
// (Throws if !typeof(T).IsFunctionPointer.)
public static FunctionHook Create<T>(void* target, T hook, object? state = null)
where T : delegate* /* or simply : unmanaged */;
} This new API shape doesn't change the above example much - it just has to be changed to pass the function pointer type as a generic argument and remove the cast from An even more significant benefit is the type information that is now available to the If I could know from So, being able to pass a function pointer type as a generic argument here would help me solve a bunch of API safety, usability, and implementation problems all at once. |
Another reason we need this: The shiny new inline arrays (#61135) are completely unusable with pointers and function pointers, presumably due to conversion to |
As an example of this, these can be useful for methods that take an array of COM interface pointers. Here are a few used in my project:
|
This alone should be enough of a reason to seriously consider lifting this limitation. There are always workarounds, but they feel unnecessary when generics exist. |
This issue's been up for a while now, what's happening? |
It's in the "Future" milestone, that means it's not being actively worked on. |
A similar argument suggest we should support |
ref is not even part of the type in C# (that's why you have to keep saying |
That's not really true. It's a syntactic decision by the language that makes it feel like it is implicit, but |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
I agree it is not a type in the sense "the C# spec does not mention it under the category
The reason there is no syntax for dereferencing a ref is because that was the decision made. Classes are implicitly dereferenced, and there is no syntax for dereferencing them, so surely they are not types by this logic?
Why would you need to do that? A generic method that takes a |
This comment was marked as off-topic.
This comment was marked as off-topic.
I agree this becomes messy (syntactically), but there's two clear routes:
|
I agree that the C# 8 Draft specification says it is not a type. But A) that's pretty old, and B) I think it is irrelevant. There is nothing about |
This comment was marked as off-topic.
This comment was marked as off-topic.
In a generic method such as this public static void F<T>(T a)
{
T b = a;
} It would just be decided that if |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Of course you can't - because that is impossible in an open generic? To do a dereferencing-assign like that, you need to restrict If you want an open-generic function over If you want a function over You are trying to do operations specific to ref-types within the context of a generic that accepts both ref and non-ref types. That will not work |
But by this definition (" |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
You are equating my view that "ref is a type" with "ref shouldn't automatically dereference". That's not what I am saying. The only change I am suggesting is how refs interact in generic contexts. Nothing else |
Again, "types by the language" just seems to mean "in the spec". Refs being added as a type in the spec wouldn't change how the language worked, but then they would be considered types.
I do see the difference around assignment there and that is a big difference. But that doesn't feel like it is the "reason" they work with generics. They work with generics because generics were written to support classes. Generics could be written to support refs too (with the rules I mentioned earlier). The only caveat is it leave some unintuitive parts of the syntax (the fact |
This comment was marked as off-topic.
This comment was marked as off-topic.
I don't understand why considering
These are the EXACT same rules as now. No changes
I agree, this would be intuitive. But I don't see any other ways to achieve it |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
We don't write integer assignments like this x = int 10; but 10 still has type
And for
I am suggesting it is already effectively a type and should be treated as such. I don't know what definition of type you have, but I still haven't seen a reason for why |
But this would be |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Ah yes I realise what you mean. But I stand by the rest of my points |
Yeah, realised what you meant just after. I read "And you are assigning a reference to that variable" to mean "assigning a reference [RHS] to that variable [normal variable on LHS]" instead of "assigning a reference [of LHS] to that variable [normal variable on RHS]", my bad |
This comment was marked as off-topic.
This comment was marked as off-topic.
Yep
I do not in anyway see how this follows. Here is an argument using the same reasoning as this one Instead of the two types being It's irrelevant whether It's the exact same argument. Both are the compiler allowing multiple types on the RHS. Suggesting that something is not a "meaningful part of the type system" because there are SOME scenarios where it behaves as something else just doesn't make sense. If in EVERY scenario, |
It would be a completely separate feature from what this issue is proposing. Pointers in generics are relatively 'easy' to implement, while Can we please take the debate about |
You're right, I'm sorry. I'll hide my comments. |
In .NET and the latest C# language versions, it is currently allowed to have
MyStruct<T>*
andT*
(.NET allows this more generally, but C# allows it only for "unmanaged" types).However, it is not possible to have
MyStruct<T*>
. This can be "problematic" for certain types of generic code, such as being able to have aSpan<int*> x
orLazy<ID3D12Device*>
since you instead must instantiate it asIntPtr
and cast at the relevant usage sites.Going through ECMA-335, the rationale given in
II.9.4 Instantiating generic types
is:I would propose that this restriction be lifted and pointers be allowed as generic type arguments. If that is possible, I would open a corresponding proposal on dotnet/csharplang suggesting that the language allow the same.
The text was updated successfully, but these errors were encountered: