On 25/06/2021 17:22, Keith Thompson wrote:
> David Brown <
david...@hesbynett.no> writes:
>> On 25/06/2021 14:07, Ben Bacarisse wrote:
> [...]
>> If implementations were changed to make the size of "eg" the minimum
>> needed to support the bit-fields, it would break current code and
>> practice such as "struct eb" here. That alone means the idea of making
>> the containing struct have minimum size will never be put in the
>> standards. (And if there are current implementations unknown to me that
>> would make "struct eg" 16-bit here despite having 32-bit int, then that
>> would of course exclude the standards mandating the more common behaviour.)
>
> Of course changing behavior that existing code depends on is not practical.
>
>> Had "minimum size" been the commonly implemented behaviour, or required
>> by the standards, then it would have worked reasonably well. And as you
>> say, there would not be a need for a type for the bit-field, just
>> "signed" or "unsigned", or "_Bool".
>>
>> It would not have allowed control of the access sizes for volatile
>> accesses. Though this is of course implementation-dependent, specifying
>> the type in the bit-field tells the compiler what size the programmer
>> wants.
>
> I don't follow. Tells the compiler the size of what? Not of the
> bit-field itself. Can you give an example that illustrates your point
> about volatile accesses?
I did, with the godbolt link that you snipped. (If you don't like
godbolt links, I can paste it into a post, but the link makes it easy to
see the code and the generated results for whatever processor you are
familiar with.)
Another relevant link would be
<
https://github.com/ARM-software/abi-aa/blob/f52e1ad3f81254497a83578dc102f6aac89e52d0/aapcs32/aapcs32.rst#8175volatile-bit-fields--preserving-number-and-width-of-container-accesses>
This is part of the ABI for ARM, which describes how volatile bit-field
access should work on that target.
You might not be very familiar with low-level coding and hardware access
(most programmers are not). Hardware is usually memory-mapped these
days. So if you are making a driver for, say, a UART (an old-fashioned
serial port) there will be specific memory addresses for a "receive"
port, a "transmit" port, for control registers for setting the baud
rate, status registers for flags, etc. A common way to handle this is
to make a struct built up from uintN_t fixed-size types and bit-fields
for the control and status fields. Your code must be precise in how it
accesses these - reading and writing registers can trigger actions. For
example, a write to the transmit register might send out a character on
the bus. Reading from the receive register might change some flags and
collect the next character from an input buffer. So you must not access
neighbouring registers unintentionally. Equally, a register might
require a 16-bit or 32-bit access to work correctly, and reading or
writing by byte will fail. You must have full control of exactly what
sizes you are using for the accesses as well as the ordering of them.
This kind of thing is, of course, implementation dependent and outside
the scope of the C standards. But people program such things in C and
specifying the exact type for the fields is how you control the exact
size of the accesses.
>
>> And it would not have allowed the use of enumeration types in
>> bit-field declarations. (Baring implementation-specific extensions,
>> enumeration types are generally indistinguishable from "int", but their
>> use can make code clearer and allow for better static checking.)
>
> Each enumeration type is compatible with some implementation-defined
> integer type, not necessarily int. (Enumeration constants are of type
> int.) But how would it not allow the use of enumeration bit-fields?
Ben's suggestion (in the sense of what he thinks would have been a good
choice for the language, rather than how he would like the standards to
be changed) was to have only "signed" or "unsigned" as the type for
bit-fields. That rules out enumeration types.
> The current scheme, where the type affects the size of the struct, does
> make that difficult (though a language feature to specify the underlying
> type of an enum would make that easier). But something like this:
> enum booool { no, maybe, probably, yes };
(Reminds me of <
https://thedailywtf.com/articles/What_Is_Truth_0x3f_>)
> struct foo {
> enum booool : 2;
> unsigned : 6;
> };
> would make perfectly good sense under the scheme I would have preferred.
Yes, that would be possible in your scheme (but see next comment).
>
> Unexpectedly, with gcc enum booool is 32 bits (it's compatible
> with unsigned int) but struct foo is 8 bits.
That is not /entirely/ unexpected. You have no named fields in the
struct, which I believe is a constraint error in C (it triggers a
-Wpedantic warning in gcc). The sizeof of an empty struct is usually
given as 1.
If you have "enum booool b : 2;" instead, the struct has size 32 bits.
That's not always what I would like, but it is consistent with the size
of the enumeration type (given by the ABI). And for those that want
tighter control and are happy with extensions, gcc's "packed" attribute
lets you make smaller enumeration types. (Or you can use C++'s scoped
enums, that can have their underlying type specified. It might be nice
for C to copy that feature.)
>
>> I don't disagree that the system you suggest could have worked, but
>> compiler implementers have gone a different route - one that I
>> personally think works better, based on the kind of coding I personally do.
>
> Is there anything you can do with the current scheme that you couldn't
> do with mine, perhaps with the addition of explicit padding?
>
Yes - I can't tell the compiler the size for volatile accesses.
Apart from that, your scheme could work. But to me, it would be a step
backwards in clarity of code. It's like suggesting C could be
simplified by removing the binary minus operator. After all, you can
write "a - b" as "a + -b". It's true, but it is not helpful to the
language.
If I want things to be of particular sizes and alignments, I use clear
fixed-size types. I don't have to figure out counts of padding bits and
add them in. The current scheme - as supported by compilers today -
lets you write code in a manner that gives a closer correspondence
between the text you write and results you get, for low level code.