Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #62183 > unrolled thread
| Started by | asetofsymbols@gmail.com |
|---|---|
| First post | 2015-05-17 10:07 -0700 |
| Last post | 2015-05-18 00:40 -0700 |
| Articles | 20 on this page of 62 — 14 participants |
Back to article view | Back to comp.lang.c
determines if it is a signed type asetofsymbols@gmail.com - 2015-05-17 10:07 -0700
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-17 18:12 +0100
Re: determines if it is a signed type asetofsymbols@gmail.com - 2015-05-17 10:34 -0700
Re: determines if it is a signed type asetofsymbols@gmail.com - 2015-05-17 11:09 -0700
Re: determines if it is a signed type Kusalananda <kusalananda@example.com> - 2015-05-17 18:22 +0000
Re: determines if it is a signed type asetofsymbols@gmail.com - 2015-05-17 11:31 -0700
Re: determines if it is a signed type Kusalananda <kusalananda@example.com> - 2015-05-17 19:59 +0000
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-17 13:12 -0700
Re: determines if it is a signed type Morris Dovey <mrdovey@iedu.com> - 2015-05-17 19:08 -0500
Re: determines if it is a signed type Morris Dovey <mrdovey@iedu.com> - 2015-05-17 19:46 -0500
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-18 01:59 +0100
Re: determines if it is a signed type Morris Dovey <mrdovey@iedu.com> - 2015-05-17 20:10 -0500
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 00:37 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-18 21:15 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 15:15 -0700
Re: determines if it is a signed type glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-18 21:51 +0000
Re: determines if it is a signed type James Kuyper <jameskuyper@verizon.net> - 2015-05-18 18:00 -0400
Re: determines if it is a signed type glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-18 14:37 +0000
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-18 17:09 +0200
Re: determines if it is a signed type glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-18 18:14 +0000
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-18 06:34 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 00:43 -0700
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-18 13:56 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 08:00 -0700
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-18 20:35 +0100
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-18 23:08 +0200
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-18 22:32 +0100
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-19 00:44 +0200
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-19 00:05 +0100
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-19 01:43 +0200
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-19 01:08 +0100
Re: determines if it is a signed type "Bartc" <bartc@freeuk.com> - 2015-05-19 11:33 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-19 08:15 -0700
Re: determines if it is a signed type "Bartc" <bartc@freeuk.com> - 2015-05-19 19:36 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-19 11:55 -0700
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-19 11:39 +0200
Re: determines if it is a signed type "Bartc" <bartc@freeuk.com> - 2015-05-19 11:44 +0100
Re: determines if it is a signed type jacobnavia <jacob@jacob.remcomp.fr> - 2015-05-19 12:11 +0200
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-19 05:45 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-19 15:03 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-19 08:46 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-19 17:08 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-19 09:52 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-19 20:36 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-20 07:40 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-20 23:12 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-21 13:52 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-21 22:48 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-22 05:50 -0700
Re: determines if it is a signed type Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-22 23:14 +0100
Re: determines if it is a signed type jadill33@gmail.com - 2015-05-26 14:31 -0700
Re: determines if it is a signed type Tim Rentsch <txr@alumni.caltech.edu> - 2015-05-22 23:32 -0700
Re: determines if it is a signed type David Brown <david.brown@hesbynett.no> - 2015-05-20 12:26 +0200
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-19 15:28 +0100
Re: determines if it is a signed type raltbos@xs4all.nl (Richard Bos) - 2015-05-23 10:37 +0000
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 15:21 -0700
Re: determines if it is a signed type Richard Heathfield <rjh@cpax.org.uk> - 2015-05-19 00:04 +0100
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-19 08:03 -0700
Re: determines if it is a signed type David Brown <david.brown@hesbynett.no> - 2015-05-19 08:47 +0200
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-19 08:07 -0700
Re: determines if it is a signed type James Kuyper <jameskuyper@verizon.net> - 2015-05-18 11:28 -0400
Re: determines if it is a signed type Keith Thompson <kst-u@mib.org> - 2015-05-18 00:40 -0700
Page 3 of 4 — ← Prev page 1 2 [3] 4 Next page →
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-19 08:46 -0700 |
| Message-ID | <c82653e9-61b8-4591-9d81-dba647009d3b@googlegroups.com> |
| In reply to | #62285 |
On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote: > jadill..@gmail.com writes: > > > On Tuesday, May 19, 2015 at 5:39:13 AM UTC-4, jacobnavia wrote: > >> Le 19/05/2015 02:08, Richard Heathfield a écrit : > >> > unsigned long will do fine > >> > >> In most versions of linux that will be 64 bits. If yu need to be able to > >> index just 10000 records you are wasting 4 bytes. If you need binary > >> compatibility of the output of a program then you can't do that. > >> > >> But I do not want to convince you. > >> You want to stay in C89? Fine with me. > > > > Yeah, one of the strongest motivations to use stdint.h is reading/writing > > binary protocols in files/sockets. You'll be hard pressed to avoid > > problems on multiple platforms without using some mechanism for > > defining fixed width types, and stdint.h is well, the standard way > > of defining the names of those types. > > The exact-width types from stdint.h barely scratch the surface of this > problem. There are representation issues (endianness being the most > common) and packing/alignment issues if you hope to use exact-width > types to define a struct you can just fread. I very rarely do any kind of serialization at the struct level. More often it is interpreted as a sequence of fields that are read one element at a time into a struct. Most of the time, these fields are fixed-width 2's complement integers, strings, and the occasional IEEE-754 floats. > And when you have to deal with these issues the simplest solution is > very often to store the result in native types (or to use the > u?int_leastN_t types from stdint.h), quite possibly in a structure that > does not map onto the protocol bytes at all. So, if you wanted to write an interface to write a 32-bit signed integer and a 32-bit unsigned integers to a file stream, how would you accomplish that without some typedef similar to those used in stdint.h? read_int32 write_int32 I can't depend on the width of int, long, and friends, but maybe I'm missing something that you see from your perspective. The binary format doesn't care what native type it's stored in. It cares that the value is a 32-bit signed or unsigned 2's complement value. You can ignore any endianness/representation issue. > > I feel that C90 without some kind of stdint.h wrapper is not well poised > > to handle this problem space. > > That's not been my experience, unless you interpret the wrapper as being > very thin indeed. About the only really useful typedef is for something > a bit like int_least32_t since you can't assume int can store 32 bits > and you may not be prepared to pay the cost of using long. I'm not trying to propose a solution for serialization. I'm trying to offer motivation to use a stdint.h defined type. And in my experience, that motivation is often enough to desire a stdint.h wrapper when limited to C90. Best regards, John D.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-05-19 17:08 +0100 |
| Message-ID | <877fs4k0si.fsf@bsb.me.uk> |
| In reply to | #62292 |
jadill33@gmail.com writes: > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote: >> jadill..@gmail.com writes: >> >> > On Tuesday, May 19, 2015 at 5:39:13 AM UTC-4, jacobnavia wrote: >> >> Le 19/05/2015 02:08, Richard Heathfield a écrit : >> >> > unsigned long will do fine >> >> >> >> In most versions of linux that will be 64 bits. If yu need to be able to >> >> index just 10000 records you are wasting 4 bytes. If you need binary >> >> compatibility of the output of a program then you can't do that. >> >> >> >> But I do not want to convince you. >> >> You want to stay in C89? Fine with me. >> > >> > Yeah, one of the strongest motivations to use stdint.h is reading/writing >> > binary protocols in files/sockets. You'll be hard pressed to avoid >> > problems on multiple platforms without using some mechanism for >> > defining fixed width types, and stdint.h is well, the standard way >> > of defining the names of those types. >> >> The exact-width types from stdint.h barely scratch the surface of this >> problem. There are representation issues (endianness being the most >> common) and packing/alignment issues if you hope to use exact-width >> types to define a struct you can just fread. > > I very rarely do any kind of serialization at the struct level. More > often it is interpreted as a sequence of fields that are read one element > at a time into a struct. Most of the time, these fields are fixed-width > 2's complement integers, strings, and the occasional IEEE-754 floats. So how does stdint.h help you with this? >> And when you have to deal with these issues the simplest solution is >> very often to store the result in native types (or to use the >> u?int_leastN_t types from stdint.h), quite possibly in a structure that >> does not map onto the protocol bytes at all. > > So, if you wanted to write an interface to write a 32-bit signed integer > and a 32-bit unsigned integers to a file stream, how would you accomplish > that without some typedef similar to those used in stdint.h? > > read_int32 > write_int32 read_int32 would return an unsigned long and write_int32 would have an unsigned long parameter. It's possible we are talking at cross purposes here. > I can't depend on the width of int, long, and friends, but maybe I'm > missing something that you see from your perspective. > > The binary format doesn't care what native type it's stored in. It cares > that the value is a 32-bit signed or unsigned 2's complement value. You > can ignore any endianness/representation issue. What do mean I can ignore it? My point was that since you almost always have to take the representation into account, having the exact size to fread or fwrite is very much less of an advantage. You're cutting my argument out from under me! >> > I feel that C90 without some kind of stdint.h wrapper is not well poised >> > to handle this problem space. >> >> That's not been my experience, unless you interpret the wrapper as being >> very thin indeed. About the only really useful typedef is for something >> a bit like int_least32_t since you can't assume int can store 32 bits >> and you may not be prepared to pay the cost of using long. > > I'm not trying to propose a solution for serialization. I'm trying to > offer motivation to use a stdint.h defined type. And in my experience, > that motivation is often enough to desire a stdint.h wrapper when limited > to C90. You mentioned protocols in files/sockets. Maybe you could give an example that motivates of the use of stdint.h for doing that? -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-19 09:52 -0700 |
| Message-ID | <b7d0b707-df27-42df-bb8a-27c6d1702178@googlegroups.com> |
| In reply to | #62295 |
On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
> jadill..@gmail.com writes:
>
> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
> >> jadill..@gmail.com writes:
> >>
> >> > On Tuesday, May 19, 2015 at 5:39:13 AM UTC-4, jacobnavia wrote:
> >> >> Le 19/05/2015 02:08, Richard Heathfield a écrit :
> >> >> > unsigned long will do fine
> >> >>
> >> >> In most versions of linux that will be 64 bits. If yu need to be able to
> >> >> index just 10000 records you are wasting 4 bytes. If you need binary
> >> >> compatibility of the output of a program then you can't do that.
> >> >>
> >> >> But I do not want to convince you.
> >> >> You want to stay in C89? Fine with me.
> >> >
> >> > Yeah, one of the strongest motivations to use stdint.h is reading/writing
> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
> >> > problems on multiple platforms without using some mechanism for
> >> > defining fixed width types, and stdint.h is well, the standard way
> >> > of defining the names of those types.
> >>
> >> The exact-width types from stdint.h barely scratch the surface of this
> >> problem. There are representation issues (endianness being the most
> >> common) and packing/alignment issues if you hope to use exact-width
> >> types to define a struct you can just fread.
> >
> > I very rarely do any kind of serialization at the struct level. More
> > often it is interpreted as a sequence of fields that are read one element
> > at a time into a struct. Most of the time, these fields are fixed-width
> > 2's complement integers, strings, and the occasional IEEE-754 floats.
>
> So how does stdint.h help you with this?
I'll try to give an example below.
> >> And when you have to deal with these issues the simplest solution is
> >> very often to store the result in native types (or to use the
> >> u?int_leastN_t types from stdint.h), quite possibly in a structure that
> >> does not map onto the protocol bytes at all.
> >
> > So, if you wanted to write an interface to write a 32-bit signed integer
> > and a 32-bit unsigned integers to a file stream, how would you accomplish
> > that without some typedef similar to those used in stdint.h?
> >
> > read_int32
> > write_int32
>
> read_int32 would return an unsigned long and write_int32 would have an
> unsigned long parameter. It's possible we are talking at cross purposes
> here.
Probably.
> > I can't depend on the width of int, long, and friends, but maybe I'm
> > missing something that you see from your perspective.
> >
> > The binary format doesn't care what native type it's stored in. It cares
> > that the value is a 32-bit signed or unsigned 2's complement value. You
> > can ignore any endianness/representation issue.
>
> What do mean I can ignore it? My point was that since you almost always
> have to take the representation into account, having the exact size to
> fread or fwrite is very much less of an advantage. You're cutting my
> argument out from under me!
Sorry, I'll try to clear it up below.
> >> > I feel that C90 without some kind of stdint.h wrapper is not well poised
> >> > to handle this problem space.
> >>
> >> That's not been my experience, unless you interpret the wrapper as being
> >> very thin indeed. About the only really useful typedef is for something
> >> a bit like int_least32_t since you can't assume int can store 32 bits
> >> and you may not be prepared to pay the cost of using long.
> >
> > I'm not trying to propose a solution for serialization. I'm trying to
> > offer motivation to use a stdint.h defined type. And in my experience,
> > that motivation is often enough to desire a stdint.h wrapper when limited
> > to C90.
>
> You mentioned protocols in files/sockets. Maybe you could give an
> example that motivates of the use of stdint.h for doing that?
Sure, here is what I use for reading 32-bit unsigned integers in little
endian format from a file.
\code snippet
bool file_read_uint32_le( void *p, FILE *stream )
{
uint8_t buf[sizeof (uint32_t)];
size_t bytes_read;
/* argument error handling */
bytes_read = fread( buf, 1, sizeof (buf), stream );
if ( bytes_read == sizeof (uint32_t) ) {
*((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
}
else
{
if ( bytes_read > 0 ) {
fseek( stream, -(long int)bytes_read, SEEK_CUR );
}
return false;
}
return true;
}
\endcode
All I'm saying is that if instead of uint32_t I use a basic type, as
in unsigned int, unsigned long, I can't guarantee that the code will
work on platforms I care about. Maybe there are problems with this
example on certain incarnations of uint8_t and uint32_t, but I'm not
that knowledgeable to know for sure.
What I'm trying to say about format is that often times the file format
is independent of the machine that you are operating on, like a pcapng
for storing packet traffic. If I use unsigned long to read a 32-bit
integer inside that file, I can run into problems using unsigned long
specifically when the platform assigns unsigned long to an unsigned
integer of 64-bits.
Hopefully I made it a bit clearer?
Best regards,
John D.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-05-19 20:36 +0100 |
| Message-ID | <871ticjr51.fsf@bsb.me.uk> |
| In reply to | #62302 |
jadill33@gmail.com writes:
I'm just going to restart this from your example because the intervening
stuff does not help as far as I can see.
> On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
>> jadill..@gmail.com writes:
>>
>> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
>> >> jadill..@gmail.com writes:
<snip>
>> >> > Yeah, one of the strongest motivations to use stdint.h is reading/writing
>> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
>> >> > problems on multiple platforms without using some mechanism for
>> >> > defining fixed width types, and stdint.h is well, the standard way
>> >> > of defining the names of those types.
<snip>
> Sure, here is what I use for reading 32-bit unsigned integers in little
> endian format from a file.
>
> \code snippet
> bool file_read_uint32_le( void *p, FILE *stream )
> {
> uint8_t buf[sizeof (uint32_t)];
> size_t bytes_read;
>
> /* argument error handling */
>
> bytes_read = fread( buf, 1, sizeof (buf), stream );
>
> if ( bytes_read == sizeof (uint32_t) ) {
> *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
> }
> else
> {
> if ( bytes_read > 0 ) {
> fseek( stream, -(long int)bytes_read, SEEK_CUR );
> }
>
> return false;
> }
>
> return true;
> }
> \endcode
>
> All I'm saying is that if instead of uint32_t I use a basic type, as
> in unsigned int, unsigned long, I can't guarantee that the code will
> work on platforms I care about.
The use of types from stdint.h buys you nothing other than to limit the
code to those platforms that have uint32_t. The use of these types
might even prevent readers from seeing the non-portable assumptions
you've made about the implementation's int type and the possible
undefined behaviour of the shifts. (The argument -- in the reader's
head -- being that since the author is using those special types the
code must be a written to be portable.)
You could limit the damage caused by stdint.h by using only uint8_t, but
even then there will be a few odd systems where using char and unsigned
long will work where uint8_t is not available. Obviously I am not
talking about s/uint8_t/char/ and s/uint32_t/unsigned long/. The code
must be written to work with these changes, but the re-writes are not
great.
There are quote a few point I'd want to bring up if reviewing this code
but I don't think that's the point here. The question is whether the
stdint.h types have helped or not, I don't think they have here.
> Maybe there are problems with this
> example on certain incarnations of uint8_t and uint32_t, but I'm not
> that knowledgeable to know for sure.
>
> What I'm trying to say about format is that often times the file format
> is independent of the machine that you are operating on, like a pcapng
> for storing packet traffic. If I use unsigned long to read a 32-bit
> integer inside that file, I can run into problems using unsigned long
> specifically when the platform assigns unsigned long to an unsigned
> integer of 64-bits.
I made a point to exclude those simple cases where you can assume the
representation. My point was about cases like the example above where
you can't just fread and fwrite an object. If you have to shuffle the
bits, you might as well shuffle them into the most universal type that
can hold the value (unsigned long in the case above).
> Hopefully I made it a bit clearer?
Yes, clearer, but I remain un-convinced! I keep seeing code with
uint32_t in it which would be more portable without it. Not that it
usually matters, since odd machines without uint32_t are rare, and the
authors of such code almost certainly don't care about those that do
still exist.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-20 07:40 -0700 |
| Message-ID | <970ad69d-3e28-4171-955e-56f9a2dc7bf8@googlegroups.com> |
| In reply to | #62317 |
On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
> jadill..@gmail.com writes:
>
> I'm just going to restart this from your example because the intervening
> stuff does not help as far as I can see.
>
> > On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
> >> jadill..@gmail.com writes:
> >>
> >> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
> >> >> jadill..@gmail.com writes:
> <snip>
> >> >> > Yeah, one of the strongest motivations to use stdint.h is reading/writing
> >> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
> >> >> > problems on multiple platforms without using some mechanism for
> >> >> > defining fixed width types, and stdint.h is well, the standard way
> >> >> > of defining the names of those types.
>
> <snip>
> > Sure, here is what I use for reading 32-bit unsigned integers in little
> > endian format from a file.
> >
> > \code snippet
> > bool file_read_uint32_le( void *p, FILE *stream )
> > {
> > uint8_t buf[sizeof (uint32_t)];
> > size_t bytes_read;
> >
> > /* argument error handling */
> >
> > bytes_read = fread( buf, 1, sizeof (buf), stream );
> >
> > if ( bytes_read == sizeof (uint32_t) ) {
> > *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
> > }
> > else
> > {
> > if ( bytes_read > 0 ) {
> > fseek( stream, -(long int)bytes_read, SEEK_CUR );
> > }
> >
> > return false;
> > }
> >
> > return true;
> > }
> > \endcode
> >
> > All I'm saying is that if instead of uint32_t I use a basic type, as
> > in unsigned int, unsigned long, I can't guarantee that the code will
> > work on platforms I care about.
>
> The use of types from stdint.h buys you nothing other than to limit the
> code to those platforms that have uint32_t. The use of these types
> might even prevent readers from seeing the non-portable assumptions
> you've made about the implementation's int type and the possible
> undefined behaviour of the shifts. (The argument -- in the reader's
> head -- being that since the author is using those special types the
> code must be a written to be portable.)
I choose types from stdint.h because they *do* have a well defined
binary representation (two's complement, no padding bits, and a fixed
width). Many of the files I work with have those constraints (fixed
width integers, no padding, 2's complement). If my interface was written
in terms of reading into an unsigned long, I don't know whether the
unsigned long is 32-bit/64-bits, has a one's complement representation,
or has some number of padding bits.
And often times, if you do not have a stdint.h on your compiler, but the
types are 2's complement with no padding, the type definition of uint32_t
comes down to something like this.
#if (ULONG_MAX == 0xffffffffUL)
typedef unsigned long uint32_t;
#elif (UINT_MAX == 0xffffffffUL)
typedef unsigned int uint32_t;
#elif (USHRT_MAX == 0xffffffffUL)
typedef unsigned short uint32_t;
#else
# error "Platform not supported"
#endif
> You could limit the damage caused by stdint.h by using only uint8_t, but
> even then there will be a few odd systems where using char and unsigned
> long will work where uint8_t is not available. Obviously I am not
> talking about s/uint8_t/char/ and s/uint32_t/unsigned long/. The code
> must be written to work with these changes, but the re-writes are not
> great.
I'm trying to avoid re-writes by using the types in stdint.h because they
do have a fixed binary representation. If a system does not have a type
that I can define a uint32_t to, then I make a special case to add a new
function to handle it.
> There are quote a few point I'd want to bring up if reviewing this code
> but I don't think that's the point here. The question is whether the
> stdint.h types have helped or not, I don't think they have here.
I would be interested to hear your perspective if you have time, as it
seems to be pretty different than the one that I've accumulated with
what seems to be much more heavy use of stdint.h. It sounds like you
don't use the stdint.h type definitions at all in any of your code.
> > Maybe there are problems with this
> > example on certain incarnations of uint8_t and uint32_t, but I'm not
> > that knowledgeable to know for sure.
> >
> > What I'm trying to say about format is that often times the file format
> > is independent of the machine that you are operating on, like a pcapng
> > for storing packet traffic. If I use unsigned long to read a 32-bit
> > integer inside that file, I can run into problems using unsigned long
> > specifically when the platform assigns unsigned long to an unsigned
> > integer of 64-bits.
>
> I made a point to exclude those simple cases where you can assume the
> representation. My point was about cases like the example above where
> you can't just fread and fwrite an object. If you have to shuffle the
> bits, you might as well shuffle them into the most universal type that
> can hold the value (unsigned long in the case above).
I think this is where I get lost. Can you make a concrete example so
I can try to wrap my head around it.
> > Hopefully I made it a bit clearer?
>
> Yes, clearer, but I remain un-convinced! I keep seeing code with
> uint32_t in it which would be more portable without it. Not that it
> usually matters, since odd machines without uint32_t are rare, and the
> authors of such code almost certainly don't care about those that do
> still exist.
Well, if you wouldn't mind, would you define a small function to read
a structure using your frame of reference for portability?
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------------------------------------------------------+
0 | Block Type = 0x0A0D0D0A |
+---------------------------------------------------------------+
4 | Block Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Byte-Order Magic |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 | Major Version | Minor Version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
This is a partial record from a pcap section header block. The question
is what types do you use to read each of these values. All of these values
are unsigned, with 3 thirty-two bit fields, followed by 2 16-bit fields.
Everything is 2's complement, no padding bits. Why is choosing unsigned
long better than uint32_t?
Best regards,
John D.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-05-20 23:12 +0100 |
| Message-ID | <87bnheoq46.fsf@bsb.me.uk> |
| In reply to | #62341 |
jadill33@gmail.com writes:
> On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
>> jadill..@gmail.com writes:
>>
>> I'm just going to restart this from your example because the intervening
>> stuff does not help as far as I can see.
>>
>> > On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
>> >> jadill..@gmail.com writes:
>> >>
>> >> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
>> >> >> jadill..@gmail.com writes:
>> <snip>
>> >> >> > Yeah, one of the strongest motivations to use stdint.h is
>> >> >> > reading/writing
>> >> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
>> >> >> > problems on multiple platforms without using some mechanism for
>> >> >> > defining fixed width types, and stdint.h is well, the standard way
>> >> >> > of defining the names of those types.
>>
>> <snip>
>> > Sure, here is what I use for reading 32-bit unsigned integers in little
>> > endian format from a file.
>> >
>> > \code snippet
>> > bool file_read_uint32_le( void *p, FILE *stream )
>> > {
>> > uint8_t buf[sizeof (uint32_t)];
>> > size_t bytes_read;
>> >
>> > /* argument error handling */
>> >
>> > bytes_read = fread( buf, 1, sizeof (buf), stream );
>> >
>> > if ( bytes_read == sizeof (uint32_t) ) {
>> > *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
>> > }
>> > else
>> > {
>> > if ( bytes_read > 0 ) {
>> > fseek( stream, -(long int)bytes_read, SEEK_CUR );
>> > }
>> >
>> > return false;
>> > }
>> >
>> > return true;
>> > }
>> > \endcode
>> >
>> > All I'm saying is that if instead of uint32_t I use a basic type, as
>> > in unsigned int, unsigned long, I can't guarantee that the code will
>> > work on platforms I care about.
>>
>> The use of types from stdint.h buys you nothing other than to limit the
>> code to those platforms that have uint32_t. The use of these types
>> might even prevent readers from seeing the non-portable assumptions
>> you've made about the implementation's int type and the possible
>> undefined behaviour of the shifts. (The argument -- in the reader's
>> head -- being that since the author is using those special types the
>> code must be a written to be portable.)
>
> I choose types from stdint.h because they *do* have a well defined
> binary representation (two's complement, no padding bits, and a fixed
> width). Many of the files I work with have those constraints (fixed
> width integers, no padding, 2's complement). If my interface was written
> in terms of reading into an unsigned long, I don't know whether the
> unsigned long is 32-bit/64-bits, has a one's complement representation,
> or has some number of padding bits.
Right, but you've not said why that matters to you, and that seems to me
to be the only point of disagreement. Maybe you have a specific
use-case in mind where the representation of the result matters, but
that's not been made explicit yet. Maybe you worry about the wasted
space on some systems? Yes, stdint.h can help with that, though even
here uint32_t looks to be the wrong choice. In those cases I'd go for
uint_least32_t which is guaranteed to be available. What use are you
imagining for the resulting value that makes the size, padding and bit
representation important?
<snip>
>> I made a point to exclude those simple cases where you can assume the
>> representation. My point was about cases like the example above where
>> you can't just fread and fwrite an object. If you have to shuffle the
>> bits, you might as well shuffle them into the most universal type that
>> can hold the value (unsigned long in the case above).
>
> I think this is where I get lost. Can you make a concrete example so
> I can try to wrap my head around it.
I mean cases where you can assume that the machine/compiler format
matches that of the file or protocol. In cases like that, reading
directly into a know-width type (maybe even a struct made of of several
of them) is the right solution. I think the Windows world has lots of
these sorts of files (at least I think it used to -- I am out of date).
>> > Hopefully I made it a bit clearer?
>>
>> Yes, clearer, but I remain un-convinced! I keep seeing code with
>> uint32_t in it which would be more portable without it. Not that it
>> usually matters, since odd machines without uint32_t are rare, and the
>> authors of such code almost certainly don't care about those that do
>> still exist.
>
> Well, if you wouldn't mind, would you define a small function to read
> a structure using your frame of reference for portability?
I don't have such a frame of reference. The situation usually
determines the portability and use to be made of the data usually has
huge impact on the storage format.
> 0 1 2 3
> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
> +---------------------------------------------------------------+
> 0 | Block Type = 0x0A0D0D0A |
> +---------------------------------------------------------------+
> 4 | Block Total Length |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 8 | Byte-Order Magic |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 12 | Major Version | Minor Version |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>
> This is a partial record from a pcap section header block. The question
> is what types do you use to read each of these values.
That can't be answered without knowing what is needed from the code. To
take a one example, a program that copies these records may only need to
extract the length (as a value) whilst storing the whole header as an
array of bytes. Other programs might need the numerical value of the
version number components, or maybe these are defined as byte strings
anyway?
The only field I'm pretty certain of how I'd like to read is the length
field. The header appears to have a byte order indicator which is in
line with how I understand pcap to work -- the byte order can vary even
within a file.
I'm not trying to duck the question. To read the length I'd write
unsigned long
read_32bits(FILE *fp, bool msb_1st)
{
unsigned long v = 0;
for (int b = 0; b < 4; b++)
v += (unsigned long)fgetc(fp) << 24*msb_1st + b*(8 - 16*msb_1st);
return v;
}
for which I've made the byte order a parameter (since that's how pcap
works I think) and I've omitted any error checking.
This seems simple and portable and I can't see what you are doing with
this value that would break if unsigned long were to have 60 bits, or to
have padding bits or whatever else. Why make the assumptions you made
in your example? How do they help?
> All of these values are unsigned, with 3 thirty-two bit fields,
> followed by 2 16-bit fields. Everything is 2's complement, no padding
> bits. Why is choosing unsigned long better than uint32_t?
In the case of reading just the one value, it simply makes fewer
assumptions about the implementation. But I'm not claiming it's better
in general -- I am doubting that uint32_t is better in quite the number
of situations where it is used.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-21 13:52 -0700 |
| Message-ID | <006226b4-21dd-45a6-a1e8-54ee88705e3d@googlegroups.com> |
| In reply to | #62372 |
On Wednesday, May 20, 2015 at 6:12:36 PM UTC-4, Ben Bacarisse wrote:
> jadill..@gmail.com writes:
>
> > On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
> >> jadill..@gmail.com writes:
> >>
> >> I'm just going to restart this from your example because the intervening
> >> stuff does not help as far as I can see.
> >>
> >> > On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
> >> >> jadill..@gmail.com writes:
> >> >>
> >> >> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
> >> >> >> jadill..@gmail.com writes:
> >> <snip>
> >> >> >> > Yeah, one of the strongest motivations to use stdint.h is
> >> >> >> > reading/writing
> >> >> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
> >> >> >> > problems on multiple platforms without using some mechanism for
> >> >> >> > defining fixed width types, and stdint.h is well, the standard way
> >> >> >> > of defining the names of those types.
> >>
> >> <snip>
> >> > Sure, here is what I use for reading 32-bit unsigned integers in little
> >> > endian format from a file.
> >> >
> >> > \code snippet
> >> > bool file_read_uint32_le( void *p, FILE *stream )
> >> > {
> >> > uint8_t buf[sizeof (uint32_t)];
> >> > size_t bytes_read;
> >> >
> >> > /* argument error handling */
> >> >
> >> > bytes_read = fread( buf, 1, sizeof (buf), stream );
> >> >
> >> > if ( bytes_read == sizeof (uint32_t) ) {
> >> > *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
> >> > }
> >> > else
> >> > {
> >> > if ( bytes_read > 0 ) {
> >> > fseek( stream, -(long int)bytes_read, SEEK_CUR );
> >> > }
> >> >
> >> > return false;
> >> > }
> >> >
> >> > return true;
> >> > }
> >> > \endcode
> >> >
> >> > All I'm saying is that if instead of uint32_t I use a basic type, as
> >> > in unsigned int, unsigned long, I can't guarantee that the code will
> >> > work on platforms I care about.
> >>
> >> The use of types from stdint.h buys you nothing other than to limit the
> >> code to those platforms that have uint32_t. The use of these types
> >> might even prevent readers from seeing the non-portable assumptions
> >> you've made about the implementation's int type and the possible
> >> undefined behaviour of the shifts. (The argument -- in the reader's
> >> head -- being that since the author is using those special types the
> >> code must be a written to be portable.)
> >
> > I choose types from stdint.h because they *do* have a well defined
> > binary representation (two's complement, no padding bits, and a fixed
> > width). Many of the files I work with have those constraints (fixed
> > width integers, no padding, 2's complement). If my interface was written
> > in terms of reading into an unsigned long, I don't know whether the
> > unsigned long is 32-bit/64-bits, has a one's complement representation,
> > or has some number of padding bits.
>
> Right, but you've not said why that matters to you, and that seems to me
> to be the only point of disagreement. Maybe you have a specific
> use-case in mind where the representation of the result matters, but
> that's not been made explicit yet.
The main motivation to use uint32_t is that it maps exactly to the
binary data that I'm reading. I've never considered choosing unsigned
long to read 32 bits, 2's complement, no padding bits from a file.
Maybe it's an education thing, since I started learning C in the
late nineties. In fact, my first reaction to using a C90 compiler
without stdint.h was to create my own loose stdint.h wrapper (for serial
port I/O of a message protocol).
> Maybe you worry about the wasted space on some systems?
At least in my mind, it was not a motivating factor to use stdint.h types
for binary I/O.
> Yes, stdint.h can help with that, though even
> here uint32_t looks to be the wrong choice. In those cases I'd go for
> uint_least32_t which is guaranteed to be available. What use are you
> imagining for the resulting value that makes the size, padding and bit
> representation important?
If I have a file whose data maps to the format the stdint.h typedefs
represent, I prefer to use those types to represent them in memory.
I like to declare the structures in memory to follow the exact binary
format in the file as much as I can.
Exactly what scenario are you imagining where uint_least32_t is available
but not uint32_t? Have you encountered it in the wild?
> <snip>
> >> I made a point to exclude those simple cases where you can assume the
> >> representation. My point was about cases like the example above where
> >> you can't just fread and fwrite an object. If you have to shuffle the
> >> bits, you might as well shuffle them into the most universal type that
> >> can hold the value (unsigned long in the case above).
> >
> > I think this is where I get lost. Can you make a concrete example so
> > I can try to wrap my head around it.
>
> I mean cases where you can assume that the machine/compiler format
> matches that of the file or protocol. In cases like that, reading
> directly into a know-width type (maybe even a struct made of of several
> of them) is the right solution. I think the Windows world has lots of
> these sorts of files (at least I think it used to -- I am out of date).
I get your point now.
> >> > Hopefully I made it a bit clearer?
> >>
> >> Yes, clearer, but I remain un-convinced! I keep seeing code with
> >> uint32_t in it which would be more portable without it. Not that it
> >> usually matters, since odd machines without uint32_t are rare, and the
> >> authors of such code almost certainly don't care about those that do
> >> still exist.
> >
> > Well, if you wouldn't mind, would you define a small function to read
> > a structure using your frame of reference for portability?
>
> I don't have such a frame of reference. The situation usually
> determines the portability and use to be made of the data usually has
> huge impact on the storage format.
>
> > 0 1 2 3
> > 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
> > +---------------------------------------------------------------+
> > 0 | Block Type = 0x0A0D0D0A |
> > +---------------------------------------------------------------+
> > 4 | Block Total Length |
> > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> > 8 | Byte-Order Magic |
> > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> > 12 | Major Version | Minor Version |
> > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> >
> > This is a partial record from a pcap section header block. The question
> > is what types do you use to read each of these values.
>
> That can't be answered without knowing what is needed from the code. To
> take a one example, a program that copies these records may only need to
> extract the length (as a value) whilst storing the whole header as an
> array of bytes. Other programs might need the numerical value of the
> version number components, or maybe these are defined as byte strings
> anyway?
The non-length fields are used for comparison in if/else or case statements
typically.
> The only field I'm pretty certain of how I'd like to read is the length
> field. The header appears to have a byte order indicator which is in
> line with how I understand pcap to work -- the byte order can vary even
> within a file.
>
> I'm not trying to duck the question. To read the length I'd write
>
> unsigned long
> read_32bits(FILE *fp, bool msb_1st)
> {
> unsigned long v = 0;
> for (int b = 0; b < 4; b++)
> v += (unsigned long)fgetc(fp) << 24*msb_1st + b*(8 - 16*msb_1st);
> return v;
> }
>
> for which I've made the byte order a parameter (since that's how pcap
> works I think) and I've omitted any error checking.
>
> This seems simple and portable and I can't see what you are doing with
> this value that would break if unsigned long were to have 60 bits, or to
> have padding bits or whatever else. Why make the assumptions you made
> in your example? How do they help?
Just mapping the binary representation of the data in a file to the
closest fit type that I can declare. The name of the integer type
uint32_t (imo) more accurately reflects the type of data than
'unsigned long'. To ask the same question in a related way, do you
use 'int' instead of 'bool' for boolean types, or do you define a
wrapper for 'bool' in the case that the type definition doesn't exist?
> > All of these values are unsigned, with 3 thirty-two bit fields,
> > followed by 2 16-bit fields. Everything is 2's complement, no padding
> > bits. Why is choosing unsigned long better than uint32_t?
>
> In the case of reading just the one value, it simply makes fewer
> assumptions about the implementation. But I'm not claiming it's better
> in general -- I am doubting that uint32_t is better in quite the number
> of situations where it is used.
Here's an excerpt out of the Wireshark developer's guide.
\quote
Don't use "long" to mean "signed 32-bit integer", and don't use
"unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits
long on many platforms. Use "gint32" for signed 32-bit integers and use
"guint32" for unsigned 32-bit integers.
\endquote
The only reason I mention it is that it's an example of what represents
the attitude of a somewhat sizable developer community whose focus is on
translating the contents of network packets into human readable text.
Okay, you say you're not claiming unsigned long is better in general,
but you've spent quite a bit of time claiming that uint32_t is a poor
choice of a data type, even for binary I/O in cases where the binary
data matches the exact representation. What qualifies as a valid use
of uint32_t if binary I/O is not one of them? Bit-field definitions?
Best regards,
John D.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-05-21 22:48 +0100 |
| Message-ID | <87lhghk3ex.fsf@bsb.me.uk> |
| In reply to | #62432 |
jadill33@gmail.com writes:
> On Wednesday, May 20, 2015 at 6:12:36 PM UTC-4, Ben Bacarisse wrote:
>> jadill..@gmail.com writes:
>>
>> > On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
>> >> jadill..@gmail.com writes:
>> >>
>> >> I'm just going to restart this from your example because the intervening
>> >> stuff does not help as far as I can see.
>> >>
>> >> > On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
>> >> >> jadill..@gmail.com writes:
>> >> >>
>> >> >> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
>> >> >> >> jadill..@gmail.com writes:
>> >> <snip>
>> >> >> >> > Yeah, one of the strongest motivations to use stdint.h is
>> >> >> >> > reading/writing
>> >> >> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
>> >> >> >> > problems on multiple platforms without using some mechanism for
>> >> >> >> > defining fixed width types, and stdint.h is well, the standard way
>> >> >> >> > of defining the names of those types.
>> >>
>> >> <snip>
>> >> > Sure, here is what I use for reading 32-bit unsigned integers in little
>> >> > endian format from a file.
>> >> >
>> >> > \code snippet
>> >> > bool file_read_uint32_le( void *p, FILE *stream )
>> >> > {
>> >> > uint8_t buf[sizeof (uint32_t)];
>> >> > size_t bytes_read;
>> >> >
>> >> > /* argument error handling */
>> >> >
>> >> > bytes_read = fread( buf, 1, sizeof (buf), stream );
>> >> >
>> >> > if ( bytes_read == sizeof (uint32_t) ) {
>> >> > *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
>> >> > }
>> >> > else
>> >> > {
>> >> > if ( bytes_read > 0 ) {
>> >> > fseek( stream, -(long int)bytes_read, SEEK_CUR );
>> >> > }
>> >> >
>> >> > return false;
>> >> > }
>> >> >
>> >> > return true;
>> >> > }
>> >> > \endcode
>> >> >
>> >> > All I'm saying is that if instead of uint32_t I use a basic type, as
>> >> > in unsigned int, unsigned long, I can't guarantee that the code will
>> >> > work on platforms I care about.
>> >>
>> >> The use of types from stdint.h buys you nothing other than to limit the
>> >> code to those platforms that have uint32_t. The use of these types
>> >> might even prevent readers from seeing the non-portable assumptions
>> >> you've made about the implementation's int type and the possible
>> >> undefined behaviour of the shifts. (The argument -- in the reader's
>> >> head -- being that since the author is using those special types the
>> >> code must be a written to be portable.)
>> >
>> > I choose types from stdint.h because they *do* have a well defined
>> > binary representation (two's complement, no padding bits, and a fixed
>> > width). Many of the files I work with have those constraints (fixed
>> > width integers, no padding, 2's complement). If my interface was written
>> > in terms of reading into an unsigned long, I don't know whether the
>> > unsigned long is 32-bit/64-bits, has a one's complement representation,
>> > or has some number of padding bits.
>>
>> Right, but you've not said why that matters to you, and that seems to me
>> to be the only point of disagreement. Maybe you have a specific
>> use-case in mind where the representation of the result matters, but
>> that's not been made explicit yet.
>
> The main motivation to use uint32_t is that it maps exactly to the
> binary data that I'm reading. I've never considered choosing unsigned
> long to read 32 bits, 2's complement, no padding bits from a file.
> Maybe it's an education thing, since I started learning C in the
> late nineties. In fact, my first reaction to using a C90 compiler
> without stdint.h was to create my own loose stdint.h wrapper (for serial
> port I/O of a message protocol).
>
>> Maybe you worry about the wasted space on some systems?
>
> At least in my mind, it was not a motivating factor to use stdint.h types
> for binary I/O.
>
>> Yes, stdint.h can help with that, though even
>> here uint32_t looks to be the wrong choice. In those cases I'd go for
>> uint_least32_t which is guaranteed to be available. What use are you
>> imagining for the resulting value that makes the size, padding and bit
>> representation important?
>
> If I have a file whose data maps to the format the stdint.h typedefs
> represent, I prefer to use those types to represent them in memory.
> I like to declare the structures in memory to follow the exact binary
> format in the file as much as I can.
Yes, but I don't see why that is important to you.
> Exactly what scenario are you imagining where uint_least32_t is available
> but not uint32_t?
Obviously I am imagining exactly a situation where unit32_t does not
exist (like a 36-bit machine for example).
> Have you encountered it in the wild?
No. I've used machines where uin32_t would not exist but that was
before C99 was even written.
<snip>
>> The only field I'm pretty certain of how I'd like to read is the length
>> field. The header appears to have a byte order indicator which is in
>> line with how I understand pcap to work -- the byte order can vary even
>> within a file.
>>
>> I'm not trying to duck the question. To read the length I'd write
>>
>> unsigned long
>> read_32bits(FILE *fp, bool msb_1st)
>> {
>> unsigned long v = 0;
>> for (int b = 0; b < 4; b++)
>> v += (unsigned long)fgetc(fp) << 24*msb_1st + b*(8 - 16*msb_1st);
>> return v;
>> }
>>
>> for which I've made the byte order a parameter (since that's how pcap
>> works I think) and I've omitted any error checking.
>>
>> This seems simple and portable and I can't see what you are doing with
>> this value that would break if unsigned long were to have 60 bits, or to
>> have padding bits or whatever else. Why make the assumptions you made
>> in your example? How do they help?
>
> Just mapping the binary representation of the data in a file to the
> closest fit type that I can declare. The name of the integer type
> uint32_t (imo) more accurately reflects the type of data than
> 'unsigned long'.
Yes, it does. I didn't know that was your motivation. I thought you
were making some claim that its use helped in some more concrete way
than what it reflects.
> To ask the same question in a related way, do you
> use 'int' instead of 'bool' for boolean types, or do you define a
> wrapper for 'bool' in the case that the type definition doesn't exist?
I do both in different contexts. I also use uint32_t at times.
>> > All of these values are unsigned, with 3 thirty-two bit fields,
>> > followed by 2 16-bit fields. Everything is 2's complement, no padding
>> > bits. Why is choosing unsigned long better than uint32_t?
>>
>> In the case of reading just the one value, it simply makes fewer
>> assumptions about the implementation. But I'm not claiming it's better
>> in general -- I am doubting that uint32_t is better in quite the number
>> of situations where it is used.
>
> Here's an excerpt out of the Wireshark developer's guide.
>
> \quote
> Don't use "long" to mean "signed 32-bit integer", and don't use
> "unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits
> long on many platforms. Use "gint32" for signed 32-bit integers and use
> "guint32" for unsigned 32-bit integers.
> \endquote
>
> The only reason I mention it is that it's an example of what represents
> the attitude of a somewhat sizable developer community whose focus is on
> translating the contents of network packets into human readable text.
They are correct to say that. I am not saying that one should use long
to mean signed 32-bit integer. If you think I disagree with this
advice, I think I must have failed to get my point across.
I think that re-writing your example to use unsigned long (and to remove
the hidden assumptions and undefined behaviour) produces a function that
is just as useful, a little simpler, and ever so slightly more portable.
The result (an unsigned long) does not meet your criteria about matching
or reflecting the data, but it can represent the values and that, to me,
is more important.
> Okay, you say you're not claiming unsigned long is better in general,
> but you've spent quite a bit of time claiming that uint32_t is a poor
> choice of a data type, even for binary I/O in cases where the binary
> data matches the exact representation. What qualifies as a valid use
> of uint32_t if binary I/O is not one of them? Bit-field definitions?
No, you are completely misrepresenting my case. Reading a known-width
number from a binary file where you know the representation matches that
of the C implementation is a fine use of these types. You did not seem
to be suggesting that. You gave an example that did not do that and did
not, as far as I could tell, benefit from the use of uint32_t.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-22 05:50 -0700 |
| Message-ID | <cc5eb8a2-1c46-44c8-a0c4-57ed2c067dfb@googlegroups.com> |
| In reply to | #62434 |
On Thursday, May 21, 2015 at 5:48:49 PM UTC-4, Ben Bacarisse wrote:
> jadill..@gmail.com writes:
>
> > On Wednesday, May 20, 2015 at 6:12:36 PM UTC-4, Ben Bacarisse wrote:
> >> jadill..@gmail.com writes:
> >>
> >> > On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
> >> >> jadill..@gmail.com writes:
> >> >>
> >> >> I'm just going to restart this from your example because the intervening
> >> >> stuff does not help as far as I can see.
> >> >>
> >> >> > On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
> >> >> >> jadill..@gmail.com writes:
> >> >> >>
> >> >> >> > On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
> >> >> >> >> jadill..@gmail.com writes:
> >> >> <snip>
> >> >> >> >> > Yeah, one of the strongest motivations to use stdint.h is
> >> >> >> >> > reading/writing
> >> >> >> >> > binary protocols in files/sockets. You'll be hard pressed to avoid
> >> >> >> >> > problems on multiple platforms without using some mechanism for
> >> >> >> >> > defining fixed width types, and stdint.h is well, the standard way
> >> >> >> >> > of defining the names of those types.
> >> >>
> >> >> <snip>
> >> >> > Sure, here is what I use for reading 32-bit unsigned integers in little
> >> >> > endian format from a file.
> >> >> >
> >> >> > \code snippet
> >> >> > bool file_read_uint32_le( void *p, FILE *stream )
> >> >> > {
> >> >> > uint8_t buf[sizeof (uint32_t)];
> >> >> > size_t bytes_read;
> >> >> >
> >> >> > /* argument error handling */
> >> >> >
> >> >> > bytes_read = fread( buf, 1, sizeof (buf), stream );
> >> >> >
> >> >> > if ( bytes_read == sizeof (uint32_t) ) {
> >> >> > *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
> >> >> > }
> >> >> > else
> >> >> > {
> >> >> > if ( bytes_read > 0 ) {
> >> >> > fseek( stream, -(long int)bytes_read, SEEK_CUR );
> >> >> > }
> >> >> >
> >> >> > return false;
> >> >> > }
> >> >> >
> >> >> > return true;
> >> >> > }
> >> >> > \endcode
> >> >> >
> >> >> > All I'm saying is that if instead of uint32_t I use a basic type, as
> >> >> > in unsigned int, unsigned long, I can't guarantee that the code will
> >> >> > work on platforms I care about.
> >> >>
> >> >> The use of types from stdint.h buys you nothing other than to limit the
> >> >> code to those platforms that have uint32_t. The use of these types
> >> >> might even prevent readers from seeing the non-portable assumptions
> >> >> you've made about the implementation's int type and the possible
> >> >> undefined behaviour of the shifts. (The argument -- in the reader's
> >> >> head -- being that since the author is using those special types the
> >> >> code must be a written to be portable.)
> >> >
> >> > I choose types from stdint.h because they *do* have a well defined
> >> > binary representation (two's complement, no padding bits, and a fixed
> >> > width). Many of the files I work with have those constraints (fixed
> >> > width integers, no padding, 2's complement). If my interface was written
> >> > in terms of reading into an unsigned long, I don't know whether the
> >> > unsigned long is 32-bit/64-bits, has a one's complement representation,
> >> > or has some number of padding bits.
> >>
> >> Right, but you've not said why that matters to you, and that seems to me
> >> to be the only point of disagreement. Maybe you have a specific
> >> use-case in mind where the representation of the result matters, but
> >> that's not been made explicit yet.
> >
> > The main motivation to use uint32_t is that it maps exactly to the
> > binary data that I'm reading. I've never considered choosing unsigned
> > long to read 32 bits, 2's complement, no padding bits from a file.
> > Maybe it's an education thing, since I started learning C in the
> > late nineties. In fact, my first reaction to using a C90 compiler
> > without stdint.h was to create my own loose stdint.h wrapper (for serial
> > port I/O of a message protocol).
> >
> >> Maybe you worry about the wasted space on some systems?
> >
> > At least in my mind, it was not a motivating factor to use stdint.h types
> > for binary I/O.
> >
> >> Yes, stdint.h can help with that, though even
> >> here uint32_t looks to be the wrong choice. In those cases I'd go for
> >> uint_least32_t which is guaranteed to be available. What use are you
> >> imagining for the resulting value that makes the size, padding and bit
> >> representation important?
> >
> > If I have a file whose data maps to the format the stdint.h typedefs
> > represent, I prefer to use those types to represent them in memory.
> > I like to declare the structures in memory to follow the exact binary
> > format in the file as much as I can.
>
> Yes, but I don't see why that is important to you.
Let me try to rephrase it with another example. If I need to read a
IEEE-754 float stored in its native binary representation from a file
or packet, and I'm on a system where the native 'float' type is
IEEE-754, and I define a type
typedef float ieee754_float
If I'm on a system where the IEEE-754 float is not the native
representation of the type 'float', there won't be a ieee754_float
typedef, and compiling the same code would result in a compiler
error due to lack of a defined type.
Would you have a problem if I had a function implementation that
only uses the type ieee754_float to read the binary value straight
from file? That is my motivation to write binary I/O functions
using uint32_t instead of unsigned long. You may of course disagree
with it, but does that communicate my point better?
> > Exactly what scenario are you imagining where uint_least32_t is available
> > but not uint32_t?
>
> Obviously I am imagining exactly a situation where unit32_t does not
> exist (like a 36-bit machine for example).
If I have a library of functions whose interface reads and writes
stdint.h types, then the lack of availability of these types is a good
indication that I need to do something special to handle these cases.
Ideally I would not call file_read_uint32_le on a system that didn't
define uint32_t, and if I did it would not compile. I'd have to define
something custom. That is why my binary I/O library of functions that
I use for the most part are defined using types from stdint.h.
> > Have you encountered it in the wild?
>
> No. I've used machines where uin32_t would not exist but that was
> before C99 was even written.
>
> <snip>
> >> The only field I'm pretty certain of how I'd like to read is the length
> >> field. The header appears to have a byte order indicator which is in
> >> line with how I understand pcap to work -- the byte order can vary even
> >> within a file.
> >>
> >> I'm not trying to duck the question. To read the length I'd write
> >>
> >> unsigned long
> >> read_32bits(FILE *fp, bool msb_1st)
> >> {
> >> unsigned long v = 0;
> >> for (int b = 0; b < 4; b++)
> >> v += (unsigned long)fgetc(fp) << 24*msb_1st + b*(8 - 16*msb_1st);
> >> return v;
> >> }
> >>
> >> for which I've made the byte order a parameter (since that's how pcap
> >> works I think) and I've omitted any error checking.
> >>
> >> This seems simple and portable and I can't see what you are doing with
> >> this value that would break if unsigned long were to have 60 bits, or to
> >> have padding bits or whatever else. Why make the assumptions you made
> >> in your example? How do they help?
> >
> > Just mapping the binary representation of the data in a file to the
> > closest fit type that I can declare. The name of the integer type
> > uint32_t (imo) more accurately reflects the type of data than
> > 'unsigned long'.
>
> Yes, it does. I didn't know that was your motivation. I thought you
> were making some claim that its use helped in some more concrete way
> than what it reflects.
>
> > To ask the same question in a related way, do you
> > use 'int' instead of 'bool' for boolean types, or do you define a
> > wrapper for 'bool' in the case that the type definition doesn't exist?
>
> I do both in different contexts. I also use uint32_t at times.
Okay. I thought your point about using unsigned long was stronger than
it actually is.
> >> > All of these values are unsigned, with 3 thirty-two bit fields,
> >> > followed by 2 16-bit fields. Everything is 2's complement, no padding
> >> > bits. Why is choosing unsigned long better than uint32_t?
> >>
> >> In the case of reading just the one value, it simply makes fewer
> >> assumptions about the implementation. But I'm not claiming it's better
> >> in general -- I am doubting that uint32_t is better in quite the number
> >> of situations where it is used.
> >
> > Here's an excerpt out of the Wireshark developer's guide.
> >
> > \quote
> > Don't use "long" to mean "signed 32-bit integer", and don't use
> > "unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits
> > long on many platforms. Use "gint32" for signed 32-bit integers and use
> > "guint32" for unsigned 32-bit integers.
> > \endquote
> >
> > The only reason I mention it is that it's an example of what represents
> > the attitude of a somewhat sizable developer community whose focus is on
> > translating the contents of network packets into human readable text.
>
> They are correct to say that. I am not saying that one should use long
> to mean signed 32-bit integer. If you think I disagree with this
> advice, I think I must have failed to get my point across.
> I think that re-writing your example to use unsigned long (and to remove
> the hidden assumptions and undefined behaviour)
Can you point out the hidden assumptions and undefined behavior? Honestly
I don't know what they are. What's hidden about uint32_t since the
standard defines the binary representation? Do you not like the void*
in the prototype and it should be declared with a uint32_t* parameter?
bool file_read_uint32_le( uint32_t *p, FILE *stream );
> produces a function that
> is just as useful, a little simpler, and ever so slightly more portable.
> The result (an unsigned long) does not meet your criteria about matching
> or reflecting the data, but it can represent the values and that, to me,
> is more important.
>
> > Okay, you say you're not claiming unsigned long is better in general,
> > but you've spent quite a bit of time claiming that uint32_t is a poor
> > choice of a data type, even for binary I/O in cases where the binary
> > data matches the exact representation. What qualifies as a valid use
> > of uint32_t if binary I/O is not one of them? Bit-field definitions?
>
> No, you are completely misrepresenting my case.
Okay, I thought you were making a stronger case not to use uint32_t in
general that you were trying to make. I still get the impression from
our discussion that you think the types from stdint.h are not the best
way to define functions to handle binary I/O for 2's complement, fixed
width, no padding integers. Am I misrepresenting your stance on this
point?
> Reading a known-width
> number from a binary file where you know the representation matches that
> of the C implementation is a fine use of these types.
This was the intended point of my original post, which is more clearly
stated in your sentence here, and was the only point I was trying to make.
> You did not seem
> to be suggesting that. You gave an example that did not do that and did
> not, as far as I could tell, benefit from the use of uint32_t.
Are you referring to my original post, or the code snippet I used?
Best regards,
John D.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-05-22 23:14 +0100 |
| Message-ID | <87lhgggszv.fsf@bsb.me.uk> |
| In reply to | #62440 |
jadill33@gmail.com writes: > On Thursday, May 21, 2015 at 5:48:49 PM UTC-4, Ben Bacarisse wrote: >> jadill..@gmail.com writes: <snip> >> > If I have a file whose data maps to the format the stdint.h typedefs >> > represent, I prefer to use those types to represent them in memory. >> > I like to declare the structures in memory to follow the exact binary >> > format in the file as much as I can. >> >> Yes, but I don't see why that is important to you. > > Let me try to rephrase it with another example. If I need to read a > IEEE-754 float stored in its native binary representation from a file > or packet, and I'm on a system where the native 'float' type is > IEEE-754, and I define a type > > typedef float ieee754_float > > If I'm on a system where the IEEE-754 float is not the native > representation of the type 'float', there won't be a ieee754_float > typedef, and compiling the same code would result in a compiler > error due to lack of a defined type. > > Would you have a problem if I had a function implementation that > only uses the type ieee754_float to read the binary value straight > from file? That is my motivation to write binary I/O functions > using uint32_t instead of unsigned long. You may of course disagree > with it, but does that communicate my point better? I made what I think is the same point some posts ago -- if you don't need to deal with "foreign" representations (the binary file matches your system's type) then using uint32_t is fine. If that was your point why did your example not do this? It read octets and put a value calculated from them into a uint32_t, where an unsigned long would have made the code (fractionally) more portable. >> > Exactly what scenario are you imagining where uint_least32_t is available >> > but not uint32_t? >> >> Obviously I am imagining exactly a situation where unit32_t does not >> exist (like a 36-bit machine for example). > > If I have a library of functions whose interface reads and writes > stdint.h types, then the lack of availability of these types is a good > indication that I need to do something special to handle these cases. But if the code had used unsigned long, no changes would be needed at all. You want stdint.h to act as an assert, but it will fire in cases where it need not fire. Reading 32 bit into an unsigned long works where uint32_t is not defined. > Ideally I would not call file_read_uint32_le on a system that didn't > define uint32_t, and if I did it would not compile. I am still puzzled by why this would be ideal. > I'd have to define > something custom. Not if the code used a universal type. How do you feel about uint_least32_t? > That is why my binary I/O library of functions that > I use for the most part are defined using types from stdint.h. <snip> >> > To ask the same question in a related way, do you >> > use 'int' instead of 'bool' for boolean types, or do you define a >> > wrapper for 'bool' in the case that the type definition doesn't exist? >> >> I do both in different contexts. I also use uint32_t at times. > > Okay. I thought your point about using unsigned long was stronger than > it actually is. No, it's really very weak, but it's based on the almost zero costs of using unsigned long. <snip> >> I think that re-writing your example to use unsigned long (and to remove >> the hidden assumptions and undefined behaviour)... > > Can you point out the hidden assumptions and undefined behavior? Honestly > I don't know what they are. What's hidden about uint32_t since the > standard defines the binary representation? uint8_t promotes to int, and you shift this promoted value in a way that is not defined (and if int is 16 bits it goes wrong in other ways too) so you are implicitly assuming that the undefined shift does what you want, and that int is at least 32 bits. C promotes types involved in most binary operations to a common type where unsigned "trumps" signed, but this does not apply to shifts. Instead, the left operand simply has the integer promotions applied. > Do you not like the void* > in the prototype and it should be declared with a uint32_t* parameter? > > bool file_read_uint32_le( uint32_t *p, FILE *stream ); I certainly prefer that (hiding the actual type behind a void * looks like a bug waiting to happen) but it's not the main issue. >> produces a function that >> is just as useful, a little simpler, and ever so slightly more portable. >> The result (an unsigned long) does not meet your criteria about matching >> or reflecting the data, but it can represent the values and that, to me, >> is more important. >> >> > Okay, you say you're not claiming unsigned long is better in general, >> > but you've spent quite a bit of time claiming that uint32_t is a poor >> > choice of a data type, even for binary I/O in cases where the binary >> > data matches the exact representation. What qualifies as a valid use >> > of uint32_t if binary I/O is not one of them? Bit-field definitions? >> >> No, you are completely misrepresenting my case. > > Okay, I thought you were making a stronger case not to use uint32_t in > general that you were trying to make. I still get the impression from > our discussion that you think the types from stdint.h are not the best > way to define functions to handle binary I/O for 2's complement, fixed > width, no padding integers. Am I misrepresenting your stance on this > point? Yes, to some extent that's a misrepresentation because I've tried to be specific about the vague term "to handle". There are kind of processing (non-portable code, code that need only work is certain environments, code that does not need to convert representations and so on) where stdint.h types are a good choice. There are other kinds of "handling" where unsigned long trumps uint32_t because it needs no special support and works everywhere. It may be a tiny advantage in practice but when the cost is zero, tiny advantages represent infinite cost/benefit gains! BTW, it's not stdint.h specifically. A lot of uses of uint32_t should, in my option, really be uses of uint_least32_t which I don't mind at all. uint_least32_t is better than unsigned long in almost all cases because it always exists and never wastes space unless space simply must be wasted. >> Reading a known-width >> number from a binary file where you know the representation matches that >> of the C implementation is a fine use of these types. > > This was the intended point of my original post, which is more clearly > stated in your sentence here, and was the only point I was trying to > make. Ah. That was obscured by your example. Just be sure we are on the same page about "representation" I mean that a simple fread into a uint32_t would do. >> You did not seem >> to be suggesting that. You gave an example that did not do that and did >> not, as far as I could tell, benefit from the use of uint32_t. > > Are you referring to my original post, or the code snippet I used? I'm not sure what you mean. I am referring to the example that did more than fread into a uint32_t. If your original post intended to highlight this simplest use of uint32_t, I certainly missed that point, but surely I can be forgiven since you then posted and example of quite another use. -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | jadill33@gmail.com |
|---|---|
| Date | 2015-05-26 14:31 -0700 |
| Message-ID | <d0a6a5c2-2bf8-4622-8e44-e8cf0fd58707@googlegroups.com> |
| In reply to | #62472 |
On Friday, May 22, 2015 at 6:14:21 PM UTC-4, Ben Bacarisse wrote: > jadill..@gmail.com writes: > > > On Thursday, May 21, 2015 at 5:48:49 PM UTC-4, Ben Bacarisse wrote: > >> jadill..@gmail.com writes: > <snip> > >> > If I have a file whose data maps to the format the stdint.h typedefs > >> > represent, I prefer to use those types to represent them in memory. > >> > I like to declare the structures in memory to follow the exact binary > >> > format in the file as much as I can. > >> > >> Yes, but I don't see why that is important to you. > > > > Let me try to rephrase it with another example. If I need to read a > > IEEE-754 float stored in its native binary representation from a file > > or packet, and I'm on a system where the native 'float' type is > > IEEE-754, and I define a type > > > > typedef float ieee754_float > > > > If I'm on a system where the IEEE-754 float is not the native > > representation of the type 'float', there won't be a ieee754_float > > typedef, and compiling the same code would result in a compiler > > error due to lack of a defined type. > > > > Would you have a problem if I had a function implementation that > > only uses the type ieee754_float to read the binary value straight > > from file? That is my motivation to write binary I/O functions > > using uint32_t instead of unsigned long. You may of course disagree > > with it, but does that communicate my point better? > > I made what I think is the same point some posts ago -- if you don't > need to deal with "foreign" representations (the binary file matches > your system's type) then using uint32_t is fine. If that was your point > why did your example not do this? It read octets and put a value > calculated from them into a uint32_t, where an unsigned long would have > made the code (fractionally) more portable. I took an example from source code that I was using. I sometimes have to deal with endian issues in file formats. I don't know if I'd write a function to read straight into a uint32_t because I could just write a fread function call. I agree that using an unsigned long would make it more portable in the example I gave. > >> > Exactly what scenario are you imagining where uint_least32_t is available > >> > but not uint32_t? > >> > >> Obviously I am imagining exactly a situation where unit32_t does not > >> exist (like a 36-bit machine for example). > > > > If I have a library of functions whose interface reads and writes > > stdint.h types, then the lack of availability of these types is a good > > indication that I need to do something special to handle these cases. > > But if the code had used unsigned long, no changes would be needed at > all. You want stdint.h to act as an assert, but it will fire in cases > where it need not fire. Reading 32 bit into an unsigned long works > where uint32_t is not defined. > > > Ideally I would not call file_read_uint32_le on a system that didn't > > define uint32_t, and if I did it would not compile. > > I am still puzzled by why this would be ideal. I wouldn't go so far as to say ideal, but I just don't think portability is not the absolute end all of writing code. I'm willing to sacrifice some portability to restrict the types of problems that I think users of my interface may face. This is why I suspect certain groups warn users against using unsigned long; they want to reduce the amount of support for when unsigned long is not the presumed 32-bits and their users make mistakes. The typical C programmer is not a comp.lang.c regular! > > I'd have to define > > something custom. > > Not if the code used a universal type. How do you feel about > uint_least32_t? I feel that it's a very "academic" type, and I've never personally seen it used "in the wild" in a couple relatively large code bases that I've had to work with. I suppose that's my only reason for not preferring it. Not a great reason, but it's the one I seem to have developed. > > That is why my binary I/O library of functions that > > I use for the most part are defined using types from stdint.h. > > <snip> > >> > To ask the same question in a related way, do you > >> > use 'int' instead of 'bool' for boolean types, or do you define a > >> > wrapper for 'bool' in the case that the type definition doesn't exist? > >> > >> I do both in different contexts. I also use uint32_t at times. > > > > Okay. I thought your point about using unsigned long was stronger than > > it actually is. > > No, it's really very weak, but it's based on the almost zero costs of > using unsigned long. > > <snip> > >> I think that re-writing your example to use unsigned long (and to remove > >> the hidden assumptions and undefined behaviour)... > > > > Can you point out the hidden assumptions and undefined behavior? Honestly > > I don't know what they are. What's hidden about uint32_t since the > > standard defines the binary representation? > > uint8_t promotes to int, and you shift this promoted value in a way that > is not defined (and if int is 16 bits it goes wrong in other ways too) > so you are implicitly assuming that the undefined shift does what you > want, and that int is at least 32 bits. Thank you, I definitely need to be more careful with the shift type. I am surprised that uint8_t promotes to int and not unsigned int for shifts! > C promotes types involved in most binary operations to a common type > where unsigned "trumps" signed, but this does not apply to shifts. > Instead, the left operand simply has the integer promotions applied. > > > Do you not like the void* > > in the prototype and it should be declared with a uint32_t* parameter? > > > > bool file_read_uint32_le( uint32_t *p, FILE *stream ); > > I certainly prefer that (hiding the actual type behind a void * looks > like a bug waiting to happen) but it's not the main issue. > > >> produces a function that > >> is just as useful, a little simpler, and ever so slightly more portable. > >> The result (an unsigned long) does not meet your criteria about matching > >> or reflecting the data, but it can represent the values and that, to me, > >> is more important. > >> > >> > Okay, you say you're not claiming unsigned long is better in general, > >> > but you've spent quite a bit of time claiming that uint32_t is a poor > >> > choice of a data type, even for binary I/O in cases where the binary > >> > data matches the exact representation. What qualifies as a valid use > >> > of uint32_t if binary I/O is not one of them? Bit-field definitions? > >> > >> No, you are completely misrepresenting my case. > > > > Okay, I thought you were making a stronger case not to use uint32_t in > > general that you were trying to make. I still get the impression from > > our discussion that you think the types from stdint.h are not the best > > way to define functions to handle binary I/O for 2's complement, fixed > > width, no padding integers. Am I misrepresenting your stance on this > > point? > > Yes, to some extent that's a misrepresentation because I've tried to be > specific about the vague term "to handle". There are kind of processing > (non-portable code, code that need only work is certain environments, > code that does not need to convert representations and so on) where > stdint.h types are a good choice. There are other kinds of "handling" > where unsigned long trumps uint32_t because it needs no special support > and works everywhere. It may be a tiny advantage in practice but when > the cost is zero, tiny advantages represent infinite cost/benefit gains! > > BTW, it's not stdint.h specifically. A lot of uses of uint32_t should, > in my option, really be uses of uint_least32_t which I don't mind at > all. uint_least32_t is better than unsigned long in almost all cases > because it always exists and never wastes space unless space simply must > be wasted. > > >> Reading a known-width > >> number from a binary file where you know the representation matches that > >> of the C implementation is a fine use of these types. > > > > This was the intended point of my original post, which is more clearly > > stated in your sentence here, and was the only point I was trying to > > make. > > Ah. That was obscured by your example. Just be sure we are on the same > page about "representation" I mean that a simple fread into a uint32_t > would do. Ok. > >> You did not seem > >> to be suggesting that. You gave an example that did not do that and did > >> not, as far as I could tell, benefit from the use of uint32_t. > > > > Are you referring to my original post, or the code snippet I used? > > I'm not sure what you mean. I am referring to the example that did more > than fread into a uint32_t. If your original post intended to highlight > this simplest use of uint32_t, I certainly missed that point, but surely > I can be forgiven since you then posted and example of quite another > use. Yeah, in hindsight, I probably should not have introduced endian-ness into the discussion. I do appreciate your time and insight, your points are making more sense to me now. Best regards, John D.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-05-22 23:32 -0700 |
| Message-ID | <kfnr3q79532.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #62341 |
jadill33@gmail.com writes:
> On Tuesday, May 19, 2015 at 3:37:06 PM UTC-4, Ben Bacarisse wrote:
>> jadill..@gmail.com writes:
>>
>> I'm just going to restart this from your example because the
>> intervening stuff does not help as far as I can see.
>>
>>> On Tuesday, May 19, 2015 at 12:08:36 PM UTC-4, Ben Bacarisse wrote:
>>>> jadill..@gmail.com writes:
>>>>
>>>>> On Tuesday, May 19, 2015 at 10:04:08 AM UTC-4, Ben Bacarisse wrote:
>>>>>> jadill..@gmail.com writes:
>>
>> <snip>
>>
>>>>>>> Yeah, one of the strongest motivations to use stdint.h is
>>>>>>> reading/writing binary protocols in files/sockets. You'll be
>>>>>>> hard pressed to avoid problems on multiple platforms without
>>>>>>> using some mechanism for defining fixed width types, and
>>>>>>> stdint.h is well, the standard way of defining the names of
>>>>>>> those types.
>>
>> <snip>
>>
>>> Sure, here is what I use for reading 32-bit unsigned integers in
>>> little endian format from a file.
>>>
>>> \code snippet
>>> bool file_read_uint32_le( void *p, FILE *stream )
>>> {
>>> uint8_t buf[sizeof (uint32_t)];
>>> size_t bytes_read;
>>>
>>> /* argument error handling */
>>>
>>> bytes_read = fread( buf, 1, sizeof (buf), stream );
>>>
>>> if ( bytes_read == sizeof (uint32_t) ) {
>>> *((uint32_t*)p) = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
>>> }
>>> else
>>> {
>>> if ( bytes_read > 0 ) {
>>> fseek( stream, -(long int)bytes_read, SEEK_CUR );
>>> }
>>>
>>> return false;
>>> }
>>>
>>> return true;
>>> }
>>> \endcode
>>>
>>> All I'm saying is that if instead of uint32_t I use a basic type,
>>> as in unsigned int, unsigned long, I can't guarantee that the code
>>> will work on platforms I care about.
>>
>> The use of types from stdint.h buys you nothing other than to limit
>> the code to those platforms that have uint32_t. The use of these
>> types might even prevent readers from seeing the non-portable
>> assumptions you've made about the implementation's int type and the
>> possible undefined behaviour of the shifts. (The argument -- in
>> the reader's head -- being that since the author is using those
>> special types the code must be a written to be portable.)
>
> I choose types from stdint.h because they *do* have a well defined
> binary representation (two's complement, no padding bits, and a
> fixed width). Many of the files I work with have those
> constraints (fixed width integers, no padding, 2's complement).
> If my interface was written in terms of reading into an unsigned
> long, I don't know whether the unsigned long is 32-bit/64-bits,
> has a one's complement representation, or has some number of
> padding bits.
>
> And often times, if you do not have a stdint.h on your compiler,
> but the types are 2's complement with no padding, the type
> definition of uint32_t comes down to something like this.
>
> #if (ULONG_MAX == 0xffffffffUL)
> typedef unsigned long uint32_t;
> #elif (UINT_MAX == 0xffffffffUL)
> typedef unsigned int uint32_t;
> #elif (USHRT_MAX == 0xffffffffUL)
> typedef unsigned short uint32_t;
> #else
> # error "Platform not supported"
> #endif
>
>> You could limit the damage caused by stdint.h by using only
>> uint8_t, but even then there will be a few odd systems where
>> using char and unsigned long will work where uint8_t is not
>> available. Obviously I am not talking about s/uint8_t/char/ and
>> s/uint32_t/unsigned long/. The code must be written to work with
>> these changes, but the re-writes are not great.
>
> I'm trying to avoid re-writes by using the types in stdint.h
> because they do have a fixed binary representation. If a system
> does not have a type that I can define a uint32_t to, then I make
> a special case to add a new function to handle it.
>
>> There are quote a few point I'd want to bring up if reviewing this
>> code but I don't think that's the point here. The question is
>> whether the stdint.h types have helped or not, I don't think they
>> have here.
>
> I would be interested to hear your perspective if you have time,
> as it seems to be pretty different than the one that I've
> accumulated with what seems to be much more heavy use of stdint.h.
> It sounds like you don't use the stdint.h type definitions at all
> in any of your code.
>
>>> Maybe there are problems with this example on certain incarnations
>>> of uint8_t and uint32_t, but I'm not that knowledgeable to know
>>> for sure.
>>>
>>> What I'm trying to say about format is that often times the file
>>> format is independent of the machine that you are operating on,
>>> like a pcapng for storing packet traffic. If I use unsigned long
>>> to read a 32-bit integer inside that file, I can run into problems
>>> using unsigned long specifically when the platform assigns
>>> unsigned long to an unsigned integer of 64-bits.
>>
>> I made a point to exclude those simple cases where you can assume
>> the representation. My point was about cases like the example
>> above where you can't just fread and fwrite an object. If you have
>> to shuffle the bits, you might as well shuffle them into the most
>> universal type that can hold the value (unsigned long in the case
>> above).
>
> I think this is where I get lost. Can you make a concrete example
> so I can try to wrap my head around it.
>
>>> Hopefully I made it a bit clearer?
>>
>> Yes, clearer, but I remain un-convinced! I keep seeing code with
>> uint32_t in it which would be more portable without it. Not that
>> it usually matters, since odd machines without uint32_t are rare,
>> and the authors of such code almost certainly don't care about
>> those that do still exist.
>
> Well, if you wouldn't mind, would you define a small function to
> read a structure using your frame of reference for portability?
>
> 0 1 2 3
> 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
> +---------------------------------------------------------------+
> 0 | Block Type = 0x0A0D0D0A |
> +---------------------------------------------------------------+
> 4 | Block Total Length |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 8 | Byte-Order Magic |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 12 | Major Version | Minor Version |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>
> This is a partial record from a pcap section header block. The
> question is what types do you use to read each of these values.
> All of these values are unsigned, with 3 thirty-two bit fields,
> followed by 2 16-bit fields. Everything is 2's complement, no
> padding bits. Why is choosing unsigned long better than uint32_t?
How you are thinking about this is a little bit confused. The
problem is how to transfer data between two different formats,
with different properties. To solve the problem, what you should
want is code that depends as /little/ as possible on how values
are represented in various C types (which corresponds to the
internal format). Here is some example code to illustrate. To
start out we will nominally use signed/unsigned long for the
internal types, but with typedefs so that the choice can be
modified later easily if that turns out to be important (and a
revision is given below). Now the code:
typedef signed long S32;
typedef unsigned long U32;
U32
next_four_unsigned_little_endian_bytes( FILE *f, int *error0 ){
unsigned char b[4] = { 0 };
const size_t k = f ? fread( b, 1, sizeof b, f ) : 0;
const U32 r = (((0UL +b[3] <<8) +b[2] <<8) +b[1] <<8) +b[0];
int dummy, *error = error0 ? error0 : &dummy;
if( k == sizeof b ) return *error = 0, r;
/* do whatever fixups are needed */
return *error = 1, -1;
}
S32
next_four_signed_little_endian_bytes( FILE *f, int *error ){
U32 u = next_four_unsigned_little_endian_bytes( f, error );
S32 low = u & 0x7fffffffL;
return u < 0x80000000U ? low : low - 0x7fffffffL - 1;
}
int
write_four_unsigned_little_endian_bytes( FILE *f, U32 u ){
unsigned char b[4] = { u, u>>8, u>>16, u>>24 };
size_t k = f ? fwrite( b, 1, sizeof b, f ) : 0;
return k != sizeof b;
}
int
write_four_signed_little_endian_bytes( FILE *f, S32 s ){
return write_four_unsigned_little_endian_bytes( f, s );
}
These routines read (or write) an external format of two's
complement, little endian, no padding bits. They do not depend
on whether internal types are big or little endian, or whether
they have padding bits, or whether they use two's complement,
ones' complement, or signed magnitude (disclaimer: with a minor
exception which I will address later). The only significant
assumption is CHAR_BIT == 8, ie that unsigned char is eight bits,
which I think fits your working assumptions. (I feel obliged to
point out that the array initialization in the second-to-last
function makes use of C99 initializer rules, but this is easily
adopted to C90 rules if that is desired.)
Note also that the function interfaces are chosen so client code
does not have to depend on knowing the type names for the values
being transferred. So the choice of type name is not forced to
propogate out into client code, eg, if client code doesn't mind
using unsigned long for those values.
Two potential problems with using signed/unsigned long for the
types here. The first is they may be too big, eg, 64 bits. The
second possible problem is only for signed long, which may not
cover the whole range of values desired (ie, it can be missing
one value). But, note that all that matters here are the ranges
of values covered. Therefore, both of these issues may be
addressed by using some preprocessor tests to determine which
base type to use:
#define CHECK_32( t ) CHECK_32_three( U## t ##_MAX, t ##_MAX, t ##_MIN )
#define CHECK_32_three( u, v, w ) ( \
u >= 4294967295 && v >= 2147483647 && -(w+1) >= 2147483647 \
)
#if CHECK_32( INT )
#define SUITABLE_32_BIT_BASE_TYPE int
#elif CHECK_32( LONG )
#define SUITABLE_32_BIT_BASE_TYPE long
#elif CHECK_32( LLONG )
#define SUITABLE_32_BIT_BASE_TYPE long long
#else
#error No suitable 32 bit base type found
#endif
typedef signed SUITABLE_32_BIT_BASE_TYPE S32;
typedef unsigned SUITABLE_32_BIT_BASE_TYPE U32;
Now we have code that is portable to almost any implementation
with CHAR_BIT == 8, especially if do the fixup for array
initialization mentioned above. And we don't have to think about
(for our program types, as opposed to external formats) questions
of endianness, padding bits, or how negative numbers are
represented - all these get taken care of by virtue of operating
with values rather than representations.
(Disclaimer: not tested, but the code given does compile
just fine.)
The two main takeaways are:
(1) External formats and internal formats are different - it's
important to deal with representation issues for the former,
but for the latter it's important to depend on representation
as little as possible.
(2) The way to avoid depending on representation for internal
types is to think primarily about values and ranges, and not
about properties like endiannes, two's vs ones' complement,
etc.
Incidentally, I agree with what I've seen of what Ben has been
saying, and I'm not contradicting that. My comments are off in
a slightly different direction, which I hope has been helpful.
Has it?
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-05-20 12:26 +0200 |
| Message-ID | <mjhnf9$m40$1@dont-email.me> |
| In reply to | #62285 |
On 19/05/15 16:03, Ben Bacarisse wrote: > jadill33@gmail.com writes: > >> On Tuesday, May 19, 2015 at 5:39:13 AM UTC-4, jacobnavia wrote: >>> Le 19/05/2015 02:08, Richard Heathfield a écrit : >>>> unsigned long will do fine >>> >>> In most versions of linux that will be 64 bits. If yu need to be able to >>> index just 10000 records you are wasting 4 bytes. If you need binary >>> compatibility of the output of a program then you can't do that. >>> >>> But I do not want to convince you. >>> You want to stay in C89? Fine with me. >> >> Yeah, one of the strongest motivations to use stdint.h is reading/writing >> binary protocols in files/sockets. You'll be hard pressed to avoid >> problems on multiple platforms without using some mechanism for >> defining fixed width types, and stdint.h is well, the standard way >> of defining the names of those types. > > The exact-width types from stdint.h barely scratch the surface of this > problem. There are representation issues (endianness being the most > common) and packing/alignment issues if you hope to use exact-width > types to define a struct you can just fread. You are right that the exact-width types cover only some of these issues, but they don't "barely scratch the surface" - they completely handle one of the biggest and most important issues (plus another smaller one). They give you full control of the sizes of the integer data, and they also give you full knowledge of the format for signed data (i.e., two's complement). I would really like the C language to incorporate a standard way of handling (specifying and changing) endian issues, but it doesn't have one. And while most compilers have ways of specifying or controlling packing and alignment, there isn't a standard method. However, you can at least use static_assert to check the size of your structs or offsets of fields to confirm that your layout is as intended. So <stdint.h> doesn't solve all the problems here, but it is a big step along the way. > > And when you have to deal with these issues the simplest solution is > very often to store the result in native types (or to use the > u?int_leastN_t types from stdint.h), quite possibly in a structure that > does not map onto the protocol bytes at all. >
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-05-19 15:28 +0100 |
| Message-ID | <mjfh8m$ufv$1@dont-email.me> |
| In reply to | #62281 |
On 19/05/15 10:39, jacobnavia wrote: > Le 19/05/2015 02:08, Richard Heathfield a écrit : >> unsigned long will do fine > > In most versions of linux that will be 64 bits. <shrug> So what? (In mine, it's actually 32 bits, but I'll take your word for it.) > If yu need to be able to > index just 10000 records you are wasting 4 bytes. If I only need to index 10000 records, unsigned int will do fine, or even unsigned short. But you said 1000000000 records, and unsigned long will do fine for that. If 1000000000 turns out to be an overestimate and the true figure is 10000 records, then I've saved 999990000 * sizeof record bytes, in which case I can probably afford to spare four extra bytes for the index object. > If you need binary > compatibility of the output of a program then you can't do that. Sure. But that's a different kettle of bananas. > > But I do not want to convince you. > You want to stay in C89? Fine with me. Actually, what I want is to impose the fewest possible constraints on the end user of the code. -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | raltbos@xs4all.nl (Richard Bos) |
|---|---|
| Date | 2015-05-23 10:37 +0000 |
| Message-ID | <556057fb.9187546@news.xs4all.nl> |
| In reply to | #62286 |
Richard Heathfield <rjh@cpax.org.uk> wrote: > On 19/05/15 10:39, jacobnavia wrote: > > If you need binary > > compatibility of the output of a program then you can't do that. > > Sure. But that's a different kettle of bananas. And one which neither <stdint.h> nor "stdint.h" will solve satisfactorily. <stdint.h> is nice, but not because of cross-platform direct binary file in- and output. Richard
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-05-18 15:21 -0700 |
| Message-ID | <87k2w54je5.fsf@kst-u.example.com> |
| In reply to | #62242 |
Richard Heathfield <rjh@cpax.org.uk> writes:
> On 18/05/15 16:00, Keith Thompson wrote:
[...]
>> The best we can do is this:
>>
>> #include <stdio.h>
>> #include <time.h>
>> #include <stdint.h>
>
> Sorry, I thought you said "portably"? If you #include <stdint.h>, you
> immediately lose portability to older C implementations.
>
> I realise that this is a price you have decided you are prepared to
> pay, but not everyone is in that position.
I did not consider implementations that don't support the current
or previous C standard. Nor did I consider implementations that
only support K&R C, or B, or BCPL.
I could have covered C90 by checking __STD_VERSION__ and falling
back to long and unsigned long rather than intmax_t and uintmax_t.
On the other hand, some pre-C99 implementations support long long as
an extension; I suppose you could check whether LLONG_MAX is defined
in <limits.h> On the other other hand, a pre-C99 implementation is
unlikely to use a type wider than long for time_t.
As far as I know, the most prominent implementation that doesn't
have at least reasonable support for C99 is Microsoft's, and I
believe it does support <stdint.h>. The situation for embedded
systems may be different.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-05-19 00:04 +0100 |
| Message-ID | <mjdr3q$o1i$1@dont-email.me> |
| In reply to | #62253 |
On 18/05/15 23:21, Keith Thompson wrote: <snip> > I did not consider implementations that don't support the current > or previous C standard. Nor did I consider implementations that > only support K&R C, or B, or BCPL. Everyone draws their own line. My line is between K&R C and C90 (i.e. all ISO C versions), and yours seems to be between C95 and C99 (i.e. all ISO C versions from C99 onwards). Curious: when C2X is released, will you drop support for C99? <snip> -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-05-19 08:03 -0700 |
| Message-ID | <87d21w4njn.fsf@kst-u.example.com> |
| In reply to | #62262 |
Richard Heathfield <rjh@cpax.org.uk> writes:
> On 18/05/15 23:21, Keith Thompson wrote:
>
> <snip>
>
>> I did not consider implementations that don't support the current
>> or previous C standard. Nor did I consider implementations that
>> only support K&R C, or B, or BCPL.
>
> Everyone draws their own line. My line is between K&R C and C90
> (i.e. all ISO C versions), and yours seems to be between C95 and C99
> (i.e. all ISO C versions from C99 onwards). Curious: when C2X is
> released, will you drop support for C99?
Support? What support?
When I post code here, I usually assume C99 or C11. I often acknowledge
that support for C99 or later is not universal. I didn't happen to do
so this time.
Here's a version that's portable to C90, C99, and C11. It can fail for
a pre-C99 implementation that supports integer types wider than long as
an extension, and that uses such a type for time_t.
#include <stdio.h>
#include <time.h>
#if __STDC_VERSION__ >= 199901L
#include <inttypes.h>
#else
typedef unsigned long uintmax_t;
typedef long intmax_t;
typedef long signed_max;
#define PRIdMAX "lu"
#define PRIuMAX "ld"
#endif
int main(void) {
time_t now = time(NULL);
if ((time_t)-1 > 0) {
/* time_t is unsigned */
printf("%" PRIuMAX "\n", (uintmax_t)now);
}
else if ((time_t)1 / 2 == 0) {
/* time_t is signed */
printf("%" PRIdMAX "\n", (intmax_t)now);
}
else {
/* time_t is floating-point */
printf("%Lf\n", (long double)now);
}
return 0;
}
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-05-19 08:47 +0200 |
| Message-ID | <mjem8j$uk4$1@dont-email.me> |
| In reply to | #62253 |
On 19/05/15 00:21, Keith Thompson wrote: > Richard Heathfield <rjh@cpax.org.uk> writes: >> On 18/05/15 16:00, Keith Thompson wrote: > [...] >>> The best we can do is this: >>> >>> #include <stdio.h> >>> #include <time.h> >>> #include <stdint.h> >> >> Sorry, I thought you said "portably"? If you #include <stdint.h>, you >> immediately lose portability to older C implementations. >> >> I realise that this is a price you have decided you are prepared to >> pay, but not everyone is in that position. > > I did not consider implementations that don't support the current > or previous C standard. Nor did I consider implementations that > only support K&R C, or B, or BCPL. > > I could have covered C90 by checking __STD_VERSION__ and falling > back to long and unsigned long rather than intmax_t and uintmax_t. > On the other hand, some pre-C99 implementations support long long as > an extension; I suppose you could check whether LLONG_MAX is defined > in <limits.h> On the other other hand, a pre-C99 implementation is > unlikely to use a type wider than long for time_t. > > As far as I know, the most prominent implementation that doesn't > have at least reasonable support for C99 is Microsoft's, and I > believe it does support <stdint.h>. The situation for embedded > systems may be different. > For embedded systems, most compilers support C99 (at least the relevant bits - often things like wide character support are missing). When using compilers that don't have <stdint.h>, people will often make their own for the compiler in question (typically an incomplete stdint.h, with just the typedefs needed for sized integer types - I doubt if anyone bothers with all the macros and range limit defines). It is also common in embedded systems to have your own sized integer types, or use ones defined by your RTOS or other bits of software (which can have a history going back to a time when <stdint.h> was less common in embedded compilers). Thus you see code with types like "u32" or "sys_uint_32_t", or similar.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-05-19 08:07 -0700 |
| Message-ID | <878uck4ndx.fsf@kst-u.example.com> |
| In reply to | #62273 |
David Brown <david.brown@hesbynett.no> writes:
[...]
> For embedded systems, most compilers support C99 (at least the relevant
> bits - often things like wide character support are missing). When
> using compilers that don't have <stdint.h>, people will often make their
> own for the compiler in question (typically an incomplete stdint.h, with
> just the typedefs needed for sized integer types - I doubt if anyone
> bothers with all the macros and range limit defines).
>
> It is also common in embedded systems to have your own sized integer
> types, or use ones defined by your RTOS or other bits of software (which
> can have a history going back to a time when <stdint.h> was less common
> in embedded compilers). Thus you see code with types like "u32" or
> "sys_uint_32_t", or similar.
Doug Gwyn wrote an implementation of <stdint.h> and several other C99
headers that work with a C90 compiler.
The files are public domain.
http://www.lysator.liu.se/%28nobg%29/c/q8/index.html
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
Page 3 of 4 — ← Prev page 1 2 [3] 4 Next page →
Back to top | Article view | comp.lang.c
csiph-web