-
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
Expose CLong, CULong, and NFloat interchange types #13788
Comments
CC. @jkoritzinsky, @AaronRobinsonMSFT, @jkotas for thoughts/opinions/etc. |
It does look like there is one unused bit on |
Would something like the below also make sense? [MarshalAs(UnmanagedType.I4)] // This HRESULT should be marshaled as a 32-bit integer
public struct HRESULT
{
internal int _hr; // contains the actual data
} |
How would this work for pointers, which are based on the native platform word size? Some native libraries, such as DirectX, have APIs which take arrays of pointers. It's hard to accept parameters like that as struct Pointer<T> where T : unmanaged
{
public T* value;
} Passing these values as |
We have number of similar suggestions to add more rules for generating interop marshallers (e.g. one for Once we have a built-time generated interop, I would be open to add as many of these new marshaling rules as necessary. |
I'm not sure that will be a sufficient way to go about this. There are a large number of interop libraries in existence today, some of which are purposefully exposing bindings in a particular way and which may not be able to change due to it being breaking or due to it being non-trivial effort. You likewise have cases where there is a lot of customization and/or configurability desired. For example:
While I think having some sort of source generator to assist with this would be nice, and while there are many existing tools that help with the binding generation process (ClangSharp, CppAst, etc); they will almost always need some kind of manual fixups, customization, etc. They also all, today, use existing interop mechanisms exposed through |
I am not suggesting that we should invent universal interop source generator. I agree that it is close to impossible to build such thing. Let me change my statement to: "I would be open to add as many of these new marshaling rules as reasonable.". This is what you can write today:
This proposal would let you avoid a bit of the boiler plate code and instead write:
My point is that boiler plate code generator should run at build time. Runtime is not a good place for it. |
So basically your proposal is the user would write the latter and then some build time tool (possibly C# source generators or an IL rewriter, so other languages can also benefit) would convert it to the former; Is that correct? I'd be interested in hearing the proposal for how that would work with function pointers and delegates, although I could conceive of that being handled by wrappers as well. I'd imagine there would also be a fairly long tail of bugs around ensuring the JIT produced good codegen for these scenarios (but fixing those would help with better codegen in the JIT overall, not just for interop). |
I expect that this would have better performance from the get-go (both startup performance and throughput). The marshaling IL generated by the runtime today is no different from what you can write in C#. However:
|
Updated the proposal based on the discussion that has been happening on #27530 (comment) |
@jkotas, @AaronRobinsonMSFT, @jkoritzinsky. I've updated the top proposal based on the most recent discussion from #27530. The proposal suggests we expose Could you let me know if everything looks good so I can mark this as |
@tannergooding I think these are good for us. What about mono? I assume we would want identical semantics so we should hear their thoughts as well. |
Xamarin defines Feel free to tag whoever you think is appropriate, I'm unsure who the relevant area owners are here. |
@lambdageek and @marek-safar Do either of you have thoughts on this API proposal? |
It looks good to me.
I am not sure how much we get by adding this - since Xamarin framework has |
I'm not sure they could switch, outside using it themselves as the internal field of their nfloat (which would just let all runtime have a consistent type to check for callconv handling). Their nfloat implements all the common methods, interfaces, and operators. |
I don't think Xamarin APIs can migrate to this version of nfloat. It'd be a noticeable breaking change for the customers so we'd end up with 2 types in "BCL" with the same name in different namespaces which I don't think is an improvement for the developers. @rolfbjarne do you agree? |
Correct, Xamarin.iOS/Mac can't migrate to these versions of What we'd need in order to avoid breaking changes is for the runtime to treat the types we define (
This runs into the ABI problem again. |
Just to clarify, while the proposed
|
Shouldn't CLong / CULong expose 64 bit values, so the full range can be marshaled on 64 Bit Linux / MacOS / iOS / Android? |
Ok @tannergooding !! I understand you. Net is not Java, correctly! But who is still Java programmer and he/she want learn with C# if they mess up like "where is NativeLong" in C#. That is problem. I want support Java-programmers into C#. "c" of clong, etc.. are okay - We need duplicate to "native" or you make easy explanation for Java programmer if they learn and understand Java and C# equivalent. Please don't be aggressive me! I just want help if Java coders write easy with translation from Java to C# like Tangible Software Solutions programs. But they don't care converter and just translate by hands. |
I definitely apologize if it came across that way. I didn't mean to come across as aggressive and just intended to explain why we don't do it like Java and the reasoning why the name
I think that is a documentation issue. We have docs like https://docs.microsoft.com/dotnet/framework/interop/marshaling-data-with-platform-invoke that provide a mapping of common types from I would expect it to be possible to provide similar for other common languages, like Java, Rust, Objective-C, or Swift and even do this for other APIs, as JNI is by far not the only API that differs. Things like the Math, Console, Networking, File I/O, and more APIs all differ significantly both in where/how they are exposed and in how they are used (something akin to a "Get started with C#, I know 'x'" rather than just "Get started with C#, I'm a beginner".) |
For what it's worth, C#9 also added native integers (i.e. |
Great job @FiniteReality nice explanation and formation with C# and nint/nuint.
But I need add protected constructor like this
But why do you not define nint with "class" and shouldn't be "struct" Please remember about ObjectRefine like Gtk2 & Gtk3 you know far but we do not implement for simple C# class like class without g_object_ref and g_object_unref. How do you resolve class from "typedef {} DefineObject"? It means as class with subclass. I hope you understand me. I am not wrongly. // EDIT: But why does Display not like struct as subclass? That is problem. |
You're using OOP wrong; if you want a handle type you should be using something like SafeHandle to properly implement your type. A |
@AaronRobinsonMSFT, this is going to require special handling so that There is already special handling for this scenario for So, should the new CLong, CULong, NFloat logic be added to |
I think CLong, CULong and NFloat should be handled as intrinsic types in the unmanaged calling convention in the JIT. I think it would be nice to finish #39294 first before starting on this one. |
I'll try to get back on #39294 so we can finish it up. I agree that these types should be handled in the JIT. That makes things like UnmanagedCallersOnly support for these types extremely easy. |
Agreed.
Yep. |
I also think a solution for ABI compatibility for the long types in unix architectures is desirable. If I can give a suggestion for naming: just call them I also have a question: where I can find the cs source for |
It is part of mono mini, also mirrored in runtime repo: runtime/src/mono/mono/mini/builtin-types.cs Line 2478 in cd622cd
|
That is what the |
"No way" seems too bold here when actually C/C++ |
Oh my god powerful teacher, @ceztko ! Good job! Linux/Unix or BSD are very different to MS-DOS. Why does young Bill Gates work hard since initial Windows Operating System with 32Bit like C/C++ formation. Is it correctly? Since my birth C++ releases initial and it works for Unix Format too. Mathematics failed to mean 64 or 32 for long.That's why I understand that now. OK we need with nint, nuint, nfloat, nshort etc for C# Unix-Net. |
While it does match the behavior for Unix platforms which are "native" in this context doesn't mean register size, which might even be 128 or more bits on some platforms. It means it matches the bitness of the underlying platform, so either 32 or 64-bits and is ultimately just extending an existing concept. With regards to the C This scenario arises quite frequently in code that was ported to Windows before the 64-bit split and less commonly after where codebases often use |
@tannergooding thank you for continuing the discussion. Actually there was no need to re-instantiate explanation for your proposed |
The API review was already done and referenced above at #13788 (comment). Everyone is welcome to participate in those reviews as they are public, this was already happened unfortunately. Thank you for sharing your feedback through it is much appreciated. |
Just as an anecdote: In our case, it's an existing, historically grown C API/ABI with roots about 25 years in the past. Back then, 64 bit for Desktop (Windows, Linux) was in the far future, and even more in the dozen or so embedded platforms which are our main target. As there's also a C++ API using the same structs and datatypes, the use of "long" also could not be upgraded to modern types like "int32_t" or "ptrsize_t" without breaking ABI compatibility for C++ clients. |
This looks like something that would benefit from a proof of concept to validate the design. For example, I notice that there are no conversion operators. The types are intentionally minimal. I wonder how well this works under practical circumstances. I also wonder if this feature carries it's weight. In this issue, there was pretty minimal discussion around that. Maybe this discussion happened elsewhere? |
Correct. Exposing these to users would be rather annoying and represent a leak in the API abstraction being exposed. The primary purpose here is to help interop do the right thing when it concerns cross platform APIs that use a C/C++ |
Rationale
There exists a few common interchange types which are variable sized. Unlike many of the other types, it is non-trivial for a user to define these types themselves and even if they did, there are some calling conventions where
struct
s are not marshaled the same as the underlyingT
they may wrap.These types include
long
andunsigned long
types defined by C/C++ which have variable size depending on the target platform/architecture. On Windows, these types are always 4-bytes and are equivalent toint
/uint
in C#. On Unix, however, these types are 4-bytes or 8-bytes depending on if you are targeting a 32-bit or 64-bit platform which makes them equivalent tonint
/nuint
.Likewise Apple defines CGFloat which is
float
on 32-bit systems anddouble
on 64-bit systems.As such, I propose we expose minimal types that are recognized by the runtime and are handled as if they were the corresponding underlying primitive type to avoid the ABI issues. These types are explicitly very minimal and do not provide "common" operations such as parsing, formatting, or even simple operators.
Proposal
The text was updated successfully, but these errors were encountered: