Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
363 views
in Technique[技术] by (71.8m points)

interop - Does C# enforce that an `unmanaged` type is "blittable"?

I'm wondering if C# 7.3's Unmanaged type constraint provides language support for enforcing that a type is blittable. According to Blittable and Non-Blittable types correctly making platform invoke calls requires asserting that the return type is blittable:

Structures that are returned from platform invoke calls must be blittable types. Platform invoke does not support non-blittable structures as return types.

The official documentation doesn't seem to state anywhere that C# enforces that an unmanaged type is "blittable", however. I'm apprehensive about assuming so because there is evidence that, at one time at least, whether a type is "blittable" could only be determined empirically.

Unmanaged type constraint documentation

The C# 7.3 "Unmanaged type constraint" proposal includes the following:

The unmanaged constraint feature will give language enforcement to the class of types known as "unmanaged types" in the C# language spec. ...

...

Blittable vs. Unmanaged

The F# language has a very similar feature which uses the keyword unmanaged. The blittable name comes from the use in Midori. May want to look to precedence here and use unmanaged instead.

Resolution The language decide to use unmanaged

...

The phrase "unmanaged types" here seems to refer to "unmanaged_type" defined in Unsafe code - Pointer Types of the draft C# 6 specification (I did not find a similar reference in the C# 5 ECMA standard). unmanaged_type is defined as follows:

An unmanaged_type is any type that isn't a reference_type or constructed type, and doesn't contain reference_type or constructed type fields at any level of nesting. In other words, an unmanaged_type is one of the following:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Any enum_type.
  • Any pointer_type.
  • Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.

I haven't found C# 7.3's unmanaged type constraint mentioned in the interop marshaling documentation; that documentation seems to predate that feature.

Blittable type documentation

Blittable and Non-blittable Types states that the following are blittable:

  • System.Byte, System.SByte, System.Int16, System.UInt16, System.Int32, System.UInt32, System.Int64, System.UInt64, System.IntPtr, System.UIntPtr, System.Single, System.Double
  • One-dimensional arrays of blittable types, such as an array of integers. However, a type that contains a variable array of blittable types is not itself blittable.
  • Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types, see Default marshaling for value types.

Is every unmanaged type "Blittable"?

At first I thought that the above definitions imply that every unmanaged type is "blittable". But I haven't found any official documentation that comes right out and states that. The closest I have found is this reply from (as I understand the timeline) before the unmanaged type constraint feature was implemented:

How is this related to Blittable Types in Interop Marshaling? The same? or similar but bit different?

It's the same.

That seems like evidence in the affirmative, but it's also rather weak evidence upon which to base design decisions. (FWIW, that exchange could also by interpreted as the meaning that all "blittable" types are unmanaged which seems less likely to be true.)

And then there's the matter of bool which isn't "blittable" and System.Boolean which is unmanaged according to the documentation. That complication is acknowledged but not directly addressed for the interop marshaler in the original proposal.

Why I'm apprehensive about this

I was not an active C# developer while the history of this seems to have unfolded and there are surely clues I've missed. Of the clues I have found, many are in the negative; there seems to have been a time when the implementation of P/Invoke resisted a total intensional definition distinguishing between blittable and non-blittable types. In other words, there were at least some versions of C# where it was non-trivial (or perhaps not possible) to confirm a priori whether the interop marshaler considered a type to be blittable. Or at least it seems that way. For example, Hans Passant wrote in 2015

There is no easy way to find out if a type is blittable, other than it not working correctly or by using the debugger and compare pointer values.

Another clue is that each of the approaches to the answers in The fastest way to check if a type is blittable? (asked in 2012) seem to be empirical. This seems like further evidence in the negative.

I suppose it could be that all unmanaged types are "blittable" but not all "blittable" types are unmanaged. But that's just speculation on my part.

question from:https://stackoverflow.com/questions/65833341/does-c-sharp-enforce-that-an-unmanaged-type-is-blittable

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Hans Passant confirmed the answer in the negative.

No, simplest counter-example is a struct with a bool field. Never blittable, okay to meet the constraint...

This seems to be a sufficient basis upon which to conclude that the answer is "no".

The second part of Hans' answer is, I think, more interesting:

It just doesn't make sense to mix the concepts...

This is counter to how I interpreted other opinions which seemed to suggest that unmanaged and "blittable" are "the same". I didn't find any elaboration of Hans' opinion that it doesn't make sense to mix the concept of unmanaged and "blittable". But I have formed an opinion of my own.

A Definition for "blittable"

The term "blittable" seems to mean slightly different things to different people and in different contexts (example 1, 2). I'll use the following definition from here for the remainder of this answer:

Blittable types are types that have the same bit-level representation in managed and native code. As such they do not need to be converted to another format to be marshaled to and from native code...

Whether a type is "blittable" depends on the particulars of the counterpart code.

By the above definition whether a type is "blittable" depends on whether the counterpart code with which you are exchanging data interprets the bits of a type in the same manner as your code. bool and BOOL provide a good example:

  • The CLI specification contains a bitwise definition of the "Boolean data type" as follows:
    • occupies 1 byte in memory
    • a bit pattern with any one or more bits set denotes true
    • a bit pattern of all zeros denotes a value of false
  • The Win32 API defines BOOL as an int and MSVC's int is 32 bits wide.
  • .Net/.Net - Because of the CLI's bitwise definition of the boolean data type, writing the bits of a bool from .Net to, for example, a file and reading those same bits as a bool requires no conversion; any .Net program will interpret the bits of such a boolean in the same manner.
  • .Net/win32 - Because of the size discrepancy between the bitwise representation of .Net bool and win32's BOOL exchanging booleans between those domains requires conversion. Without conversion, win32 APIs emitting BOOL could set only bits in the most significant 24 bits to convey true and .Net may well interpret that as bool false.

Summary

From the perspective of C#, whether a type is blittable depends on whether the counterpart code happens to interpret the bits representing that type in the same way. That will vary depending on the counterparty. In the case of .Net exchanging data with a .Net counterparty, unmanaged provides a constraint that guarantees that the bits representing the type will be correctly interpreted by the two parties.

For counterparties other than .Net, no such similar language constraints currently exist. And I wouldn't expect such a constraint to exist in C#; the counterparties are many and their interpretation of the bits representating their types are surely defined in a manner that the C# specification cannot rely upon.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...