Replies: 24 comments 75 replies
-
Looks like this would largely address #457. |
Beta Was this translation helpful? Give feedback.
-
@gafter but the fact that I see this here means that you are taking in consideration to add this to the C# language in the future? Or it is when I see "Champion" that this happens? |
Beta Was this translation helpful? Give feedback.
-
request +1, and why not just let C# has the same style like the C/C++ struct reg {
byte bit1: 1;
short bit3: 3;
ushort bit5: 5;
int bit32;
int bit64;
} |
Beta Was this translation helpful? Give feedback.
-
@gafter could be this support add in C# 8.0 candidate or C# 9.0 candidate |
Beta Was this translation helpful? Give feedback.
-
@sgf Since we're wrapping up C# 8, it doesn't make any sense to delay C# 8 by adding any features to its scope. This proposal doesn't have any LDM champion, so there is no way to move it forward at this point. Until someone on the LDM takes it up and advocates it as important enough to invest in, it won't be planned for any particular release. |
Beta Was this translation helpful? Give feedback.
-
Ok,thanks,got it.
|
Beta Was this translation helpful? Give feedback.
-
@sgf: I do like the syntax (and the proposal), public struct S1
{
public byte b : 1;
}
public struct S2
{
public byte b = 1;
}
public struct S3
{
public byte b : 1 = 1;
} EDIT: Nevermind, one cannot initialize struct fields. |
Beta Was this translation helpful? Give feedback.
-
so... is this dead in the water? Talking with C code using packed structs is a boon in IoT applications with little memory. I already rely on DotNetCore on many Beaglebone projects of mine and those are usually fairly low-level. |
Beta Was this translation helpful? Give feedback.
-
Bit fields are highly OS/CPU specific and (IMO) only work well for programming languages whose code will always be compiled for a specific target platform. Here are some problems I see for a platform independent programming language like C#:
Explicit |
Beta Was this translation helpful? Give feedback.
-
I toyed around with a bitfield struct that I want for a project, and I could see a simplified version of that declaration. Specifically: My current implementation involves declaring this struct: private struct Info
{
// Flags
private const int
HasDiedFlag = 1 << 31,
HasPromotedFlag = 1 << 30,
AllFlagMask = HasDiedFlag | HasPromotedFlag;
private int bits;
// Bring C back:tm:
public int MoveTimes
{
get => bits & ~AllFlagMask;
set => bits = (bits & AllFlagMask) | (value & ~AllFlagMask);
}
public bool HasDied
{
get => HasFlag(bits, HasDiedFlag);
set => Toggle(ref bits, HasDiedFlag, value);
}
public bool HasPromoted
{
get => HasFlag(bits, HasPromotedFlag);
set => Toggle(ref bits, HasPromotedFlag, value);
}
public void RegisterMoveTime() => bits++;
public void UnregisterMoveTime() => bits--;
private static bool HasFlag(int bits, int value) => (bits & value) != 0;
private static void Toggle(ref int bits, int value, bool toggle) => bits = Toggle(bits, value, toggle);
private static int Toggle(int bits, int value, bool toggle)
{
return toggle switch
{
true => bits | value,
false => bits & ~value,
};
}
} I came up with a more concise syntax for this declaration, like the following: private bitfield struct Info
{
public bool HasDied[^1];
public bool HasPromoted[^2];
public int MoveTimes[...]; // would become ..^2
public void RegisterMoveTime() => bits++;
public void UnregisterMoveTime() => bits--;
} Within the brackets following the property, a constant The In the above case, it is assumed that the type of the bitfield struct is bitfield struct B : ulong
bitfield struct B : 8 |
Beta Was this translation helpful? Give feedback.
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.
-
ok go ahead, I don't think the .net team will implement this feature until 2025. If not lucky enough.,Even in 2030, it may not be taken seriously. |
Beta Was this translation helpful? Give feedback.
This comment was marked as disruptive content.
This comment was marked as disruptive content.
-
Isn't this also a matter of performance? How is the C bitfield translated into assembler code? If the source generator only does the bitfiddling identical to what C does and the resulting performance is the same I would agree that a source generator would be sufficient. That is what the source generator could solve, but it looks a bit unnatural to do this with methods. If partial properties would exists that would be the better choice. I have to read gigabytes of data that are bitfields, fiddling them out "works" but is really not nice (but I have never measured the difference between a direct read and the bit-fiddling). I think currently in the case of this interop many people are using C++/CLI which brings its own complications... I have never met someone who really likes it. Nevertheless, if performance could be improved there are few areas where it could help:
|
Beta Was this translation helpful? Give feedback.
-
Recently I'm playing with Vulkan ray tracing with my own generated C# library. At first I was crashing the driver randomly. After days of API dumping and comparing I found that their VkAccelerationStructureInstanceKHR has bit fields defined. My buggy My mapping struct was generated anyways so I can add code in the generator to emit bit fiddling property accessors. But it would be nice that C# had similar features so the code gen could be much straight forward. |
Beta Was this translation helpful? Give feedback.
-
Funny how it never rains, it pours. :-) Just last week I found myself in gfx lib situation myself where this issue came up, though voxel-generation instead of Vulkan. I encountered a struct with a bunch of bool's. No biggie, right? Well, if you deal with 100 million instances it can be. So, I changed those bool's to be properties (memory pressure had become more important than the performance hit) and then were about to add the backing-store's as bitfields. Oh... Yeah, I was forced to write that usual ugly mass of repetetive manual code for each and every accessor, and thanks to the wonderful feature "no macros" one can't even lessen the pain even a little bit. That sucks. Thinking back through C# code I've seen over the last decade, and the number of hand-rolled implementations of this language deficiency, I can state with 100% certainty that this deficiency has hurt people and code for a long time. So yeah, this language feature def. has my But I wonder... most if not all use-cases I have encountered in C# have been around treating data as packed bool's. This scenario is semantically different from e.g. interfacing with hardware or dealing with protocol data, where values can be represented by 2 or more bits, and some scenarios can allow for both signed and unsigned quantities (e.g. RLE-encoded data). Could that differentiation possibly warrant two separate propsals, and data types? It could seriously lower the bar to specification and implementation of the simpler case. In case someone is tempted to "solve" the packed-bools at a combination of library- + compiler level (like ValueTuple), forget it. You would have to manually write (at least) 64 overrides for each of the 1-64 generic's. 🤣 A few random ideas on the design:
If limiting the following to the packed-bool's scenario, could something like the following provide food for thought, or even work? Since this scenario is much, much simpler than having variable-size and variable-type packed fields, it makes sense to me to explore this first. Working name Considerations:
(Please note that everything herein is written directly in a web browser. No code is tested in any way or form. Consider this a thought experiment.) This leads to something like:
I now might have gone overboard a bit in the refactoring and design. 😄 Fingers crossed it still makes sense.
Now let's explore how a concrete case could look:
The source construct generating the declaration
that in the concrete example would result in
phew Just my 0.02. 😄 |
Beta Was this translation helpful? Give feedback.
-
We need this ASAP i'm tired making huge work-arounds to manage struct with bit fields |
Beta Was this translation helpful? Give feedback.
-
Seeing the recent surge in activity, alongside having given my opinion in the past, I've started to lean towards the generator side of the topic. Adding such a feature would be purely cosmetic for nothing but a very apparent case in a niche area, which involves low-level bit management. This feature would not enable previously unavailable constructs being expressed in legal C#, and thus a generator sounds more reasonable. That being said, generators are barely being developed, and from what I've heard elsewhere they are still very immature in nature, with the major annoyances that I have also encountered myself being compilation performance, IDE compatibility and getting them to work in both the producer and the consumer side. Once generators mature and the tooling becomes friendlier, this will be something completely trivial to build and package. For now my only guess would be that nobody bothered to write a generator for this, and many other proposals that deserve their generator for two reasons:
|
Beta Was this translation helpful? Give feedback.
-
Out of interest,has anyone measured the difference between the hassle and complexity of implementing a primary constructor versus implementing a Bitfield struct? |
Beta Was this translation helpful? Give feedback.
-
One goal is to create a source generator, but the runtime produces the same code as if it would be written manually. To improve performance, as said above, there are special CPU instructions for bit-fiddling. Would it make sense to enhance the |
Beta Was this translation helpful? Give feedback.
-
I wrote a library for bitfields. In the absence of syntactic sugar, it's fairly simple. |
Beta Was this translation helpful? Give feedback.
-
For anyone who stumbles across this thread like I have, it seems the best solution for this nowadays would be to leverage a BitVector32 or a BitArray. It might not give the nice struct-y definition syntax, but it should at least address the problem with fewer bit-shifts and |
Beta Was this translation helpful? Give feedback.
-
Bitfields are a constant pain when doing P/Invoke. C programmers love their bitfields, and Win32 is no exception. When working with serial ports, one must use the |
Beta Was this translation helpful? Give feedback.
-
@fanoI commented on Fri Oct 23 2015
Support for Struct Bitfileds could be added to the language?
Having to talk with low level hardware in C/C++ is really natural to write something as:
to write in the VI bit one simply does:
reg.bit6 = 1
and get the value of the bit itself it is easy.When it is time to write to the device you simple cast the the thing to char (they are 8 bit that is a byte) and use the write method.
But how this struct could be represented in C#?
For example in this way:
The compiler probably will inject some hidden method to simulate the access to a reg bit (it will probably create a a normal 8 bit hidden field and uses bitmask behind the scenes).
Obviously BitfieldLength could assume any value maybe the only restriction we can have is that the size in byte of the struct could be a multiple of 8 (while pack 0 is written in the struct attributes above it is should not implicate that the compiler cannot add an hidden padding field at the end of the struct to align to the size of a native type if it needs to do this).
With this bitfield struct in place one could easily create exotic integer values as Int24 o Int128 this for example is an Int24 struct:
At this point one could use as a normal Int32:
Int24 a = 42;
probably this will trigger an unwanted conversion from Int32 and for new int types > 64 their values cannot be represented as literals so probably this could be coupled with the possibility to define our constant.@Joe4evr commented on Fri Oct 23 2015
I'm pretty sure this is a dupe, but I can't find the previous issue off-hand.
Anyway, getting the value of a single bit in the CLR is actually not so easy. For reference here's a good piece about booleans in the CLR, the most relevant part of which is:
The closest thing you're going to get to this is Binary Literals (#215, code for which is already checked in), but I wouldn't hold my breath for awkwardly sized integer types.
@HaloFour commented on Fri Oct 23 2015
@Joe4evr
It was over on the CoreCLR repo: #1635
@fanoI commented on Fri Oct 23 2015
Yes I reported the issue in the CoreCLR repo but they said that was better to do this in the language that modify the CLR so I cross posted it here.
However the idea is that the Roslyn compiler transforms that struct in something more digestible by the CLR giving to the developer the illusion that he can access the bits directly when in reality bitmasking is done (on one byte in the
reg
example), I don't pretend that the .NET makes bit really accessible as indeed this would not map with any CPU existing.I imagine GCC itself does a sort of transformation internally as for sure my X86 does not support bit addressing.
This is a possible way to transform it:
Implemented in this way it will be better that what GCC does as it retains the "illusion" that it
bit1
is a bit and doesn't likes if you try to pass a "bit" as a pointer to a function, C# would pass a "bitmasked" byte as a reference / output parameter and should not have problems.@DerpMcDerp commented on Tue Feb 23 2016
C's bitfields aren't designed that well. I'd recommend basing a modern bitfield proposal on the
BitField
template ( https://github.com/v8/v8/blob/5.0.71/src/utils.h#L244 ) from Google Chrome's v8 JavaScript engine which fixes some of the problems:e.g.
1
is the starting bit position of the slice2
is the bit length of the slicechar
means how you want to interpret the bits of the bit slice asdecltype(bit_field_)
is the underlying type of the sliceThen you can do stuff like:
The reason why this is way superior than C bitfields is:
BitField
s over each other which is useful for parallel operations and for interpreting a superset bits as something else, there is no non-DRY way in C to do something like:C bitfields make this sort of thing such a pain that it's almost always better to resort to your own masking.
BitField
s are ad-hoc. You can treat any arbitrary integer like value as a bitfield just by overlaying theBitField
view over it. With C, you're required tomemcpy
the data to the bitfield then use the bitfield as if it were a view.BitField
view to have the compiler generate bitmasks and let you query things like min/max values. Doing it the C way would require something like new keywords orconstexpr
to recover that kind of info:@GeirGrusom commented on Tue Feb 23 2016
Why couldn't you just use a pointer, which is a view in itself?
@DerpMcDerp commented on Wed Feb 24 2016
Type punning through a pointer/reference is undefined behavior since it violates C/C++'s strict type aliasing rules. Modern compilers break your code on high optimization levels if you do that.
@GeirGrusom commented on Sun Feb 28 2016
How is
memcpy
any less of a type violation than pointers? Aren't you just doing the same thing in a different storage location?edit: nevermind. Strict aliasing is the rule that C and C++ compilers assume that two different pointers of different types don't point to the same data, so it may optimize away code that depends on them pointing to the same thing producing a very hard to debug error. Copying resolves this.
Beta Was this translation helpful? Give feedback.
All reactions