CBFalconer wrote:> David Brown wrote: > ... snip ... >> Secondly, I was suggesting that if you want portable code, you have >> to use size-specific integer types. Using <stdint.h> is an easy >> way to get that - otherwise, a common format header file that is >> adapted for the compiler/target in question is a useful method. >> It doesn't really matter whether you use "uint32_t" from <stdint.h>, >> or have a "typedef unsigned long int uint32_t" in a common header >> file - nor does it matter if you give the type your own name. But >> it *does* matter that you have such types available in your code. > > I disagree. "unsigned long" is guaranteed to have at least 32 > bits. Similarly "unsigned int" is guaranteed at least 16 bits, as > is "unsigned short". "unsigned char" is at least 8 bits. These > types are ALWAYS available, but the various 'uint32_t' etc. types > are not. Even if present, they are only present on a C99 compiler > system. Why abandon portability unnecessarily? >As you say, an "unsigned long int" is guaranteed (by any strictly compliant compiler) to have *at least* 32 bits. But nowhere is it specified to have *at most* 32 bits. Thus "unsigned long int" is useless if I want *exactly* 32 bits. That is why the <stdint.h> types were defined, and that is why they are important if you want portable code that works on a wide range of targets (at least, for architectures that can support 8, 16 and 32-bit data - targets with 36 bit cpus, for example, will always be problematic for portable code). When you are using compilers without <stdint.h>, you need to write your own header with suitable typedefs - then you can keep your source code portable.
Delay Routine: Fully-portable C89 if possible
Started by ●October 9, 2007
Reply by ●October 11, 20072007-10-11
Reply by ●October 11, 20072007-10-11
Hans-Bernhard Br�ker wrote:> Martin Wells wrote: >> John: > >>> For example, in C99 I could define a 32 bit hardware register like >>> this: > >>> #include <stdint.h> >>> #define PORT (*(volatile uint32_t *)(0x1FFF0000)) > >> This will only work on implementations that actually have an unsigned >> integer type that has exactly 32 value representational bits. > > I suspect you'll find this hard to believe, but: that's actually a good > thing. A platform that has no such type can't run that code as > designed, so there's no point for it to compile on that platform. It > should fail. > >> On other implementations, it won't compile. Best to use >> uint_least32_t (at least 32-Bits). > > Not in this particular case. If the platform doesn't have 32-bit > integers, it can't have 32-bit hardware registers, so it shouldn't > compile this code. >It's perhaps more accurate to talk about the platform "supporting" 32-bit integers - it's not clear whether you are trying to say that the cpu in question must be at least 32 bits (which is of course not the case - your code would work perfectly well on an 8-bit cpu, which regularly have 16-bit hardware registers and perhaps also 32-bit hardware registers) or just that it can work with 32-bit data (as distinct from, say, a 36-bit machine).>> unsigned long is guaranteed to be atleast 32-Bit, so that's fine. If >> you wanted to go easy on space consumption, you could still use C89 >> and use macros: >> >> #if VALUE_BITS(char unsigned) >= 32 >> typedef char unsigned uint_least32_t; > > It would be rather nice if a VALUE_BITS like that were a C89 standard > functionality, wouldn't it? Well, sorry, it's not. And not only that: > it can't even be implemented in C89. There's only CHAR_BIT (in > <limits.h>), and sizeof() --- but the latter doesn't work in > preprocessor instructions. > > There are reasons <stdint.h> was made part of the standard. One of them > is that it's quite hard to implement its functionality unless you're the > compiler implementor.
Reply by ●October 11, 20072007-10-11
John Devereux wrote:> "Ulf Samuelsson" <ulf@a-t-m-e-l.com> writes: > >> "John Devereux" <jdREMOVE@THISdevereux.me.uk> skrev i meddelandet >> news:87hcl0q9e6.fsf@cordelia.devereux.me.uk... >>> Martin Wells <warint@eircom.net> writes: >>> >>>> David: >>>> >>>>> Most >>>>> importantly, it gives you "stdint.h" and types like "uint32_t" so that >>>>> you can avoid unspecific non-standardised types like "long unsigned" >>>>> (which should always be written "long unsigned int"). >>>> "long unsigned int" is a part of C89. >>>> >>>> Perhaps you were on about "long long unsigned int"? (which is a part >>>> of C89 but not C99) >>>> >>>> As far as any compliant C89 compiler is concerned, "long unsigned" and >>>> "long unsigned int" are the same thing. If the compiler doesn't accept >>>> it, then it isn't a C89 compiler. >>>> >>>> Even if it were worth switching to C99 (which I don't think it is), I >>>> still wouldn't because it's so poorly implemented today. >>> No, he is saying that he prefers being able to use uint32_t, instead >>> of, for example, long unsigned. >>> >>> I personally don't agree though. At least in my own work I have not >>> found a real-life situation where these types are an >>> improvement. Basically, if you are worried about the exact widths of >>> types, then that part of the program is likely non-portable anyway, so >>> the new types don't help much. >>> >>> For example, in C99 I could define a 32 bit hardware register like >>> this: >>> >>> #include <stdint.h> >>> #define PORT (*(volatile uint32_t *)(0x1FFF0000)) >>> >>> But in fact this code will likely be useless on some hypothetical >>> other CPU anyway. I can just as easily rely on ints being 32 bit on my >>> platform, and do >>> >>> #define PORT (*(volatile unsigned long *)(0x1FFF0000)) >>> >>> >> Its a terribly good example to try to access H/W functionality >> when we are talking about portability - not. > > That is kind of what I was saying! Most cases where I want to use > exact widths are not portable anyway, so the "exact-width" types are > no help. > >> Main problem is likely to be the int, which is quite often 16 bit >> on a small micro and 32 bit on a larger micro. >> char's are sometimes set to unsigned char on small micros, >> and to be able to guarantee signedness regardless of >> compiler options is an improvement. > > You can already guarantee signedness using "signed" or "unsigned" as > needed. > > I.e., I don't see what is wrong with using > > "signed char" when you need signed > "unsigned char" when you need unsigned > "char" when you don't care. > > If you start using e.g. uint8_t everywhere then you get into trouble > with the library functions that expect plain char. >When writing programs, it's important that they are understandable and make sense when read. Thus the type "char" makes sense for holding a character, or as part of a string - it does not make sense for holding a number, for example. The types "unsigned char" and "signed char" do not make any sense at all, and thus should only appear hidden away in a header - typedef'ed names such as "uint8_t" and "sint8_t" make vastly more sense.
Reply by ●October 11, 20072007-10-11
Tom Lucas wrote:> "David Brown" <david@westcontrol.removethisbit.com> wrote in message > news:470ccdb0$0$3218$8404b019@news.wineasy.se... >> John Devereux wrote: >>> David Brown <david@westcontrol.removethisbit.com> writes: >>> > <snip> >> First off, I don't use printf or friends very often (I write small >> embedded systems). Secondly, if I *do* use printf (more likely >> snprintf), I use gcc which will type-check the parameters against the >> format so that any mistakes are caught - although with any variable >> parameter function, you've lost much of C's already limited type >> checking. Thirdly, I occasionally have to cast the parameters >> explicitly so that I can be sure there are no mistakes. >> >>> Doesn't that imply a whole new set of things to worry about? >>> >> I would not say so, no. > > I've come a cropper using gcc for ARM by passing an integer to a sprintf > that was expecting float. Caused my system to reset and took quite a > long time to find. > >The warnings will only work if enabled (-Wformat, or -Wall), and will depend somewhat on the library you are using (you probably need appropriate function attributes in the declaration of sprintf, although it's possible that gcc will add them automatically for standard *printf functions): http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat-214 http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#index-Wformat-2019 mvh., David
Reply by ●October 11, 20072007-10-11
tom8192@hotmail.com (Tom) writes:> cbfalconer@maineline.net wrote: > >David Brown wrote: > >> > >.... snip ... > >> > >> Secondly, I was suggesting that if you want portable code, you have > >> to use size-specific integer types. Using <stdint.h> is an easy > >> way to get that - otherwise, a common format header file that is > >> adapted for the compiler/target in question is a useful method. > >> It doesn't really matter whether you use "uint32_t" from <stdint.h>, > >> or have a "typedef unsigned long int uint32_t" in a common header > >> file - nor does it matter if you give the type your own name. But > >> it *does* matter that you have such types available in your code. > > > >I disagree. "unsigned long" is guaranteed to have at least 32 > >bits. Similarly "unsigned int" is guaranteed at least 16 bits, as > >is "unsigned short". "unsigned char" is at least 8 bits. > > On the compiler that I use, CCS for PIC16, the default for an "unsigned int" > is 8 bits while an "unsigned long" is 16 bits and an "unsigned short" is 1 > bit. If you want 32 bits you have to use an "int32". All types are by > default unsigned unless you explicitly declare them as signed. > > Call it broken or non-compliant if you want, but it is what it is and it's > been like that for several years and through many revisions of the complier. > > I've also used gcc, C2C, Hi-tech, Avocet, and probably a few others on various > platforms. Not all of them follow the rules. > > I gave up this fight a long time ago. If the compiler that I'm using doesn't > have <stdint.h> then I just write my own. This way I'm guaranteed portability > no matter what. I should mention that I tend to do a fair bit of development > and testing on WIN32 and then just copy&paste the code as-is into the PIC > compiler. Haven't encountered any portability problems yet.Does anyone have a public domain copy of stdint.h? Any number of people speak of rolling their own, but that seems a little silly if it's advocated that as many people as possible use stdint.h.
Reply by ●October 11, 20072007-10-11
mojaveg@mojaveg.lsan.mdsg-pacwest.com (Everett M. Greene) writes:> tom8192@hotmail.com (Tom) writes:[...]> > Does anyone have a public domain copy of stdint.h? Any > number of people speak of rolling their own, but that > seems a little silly if it's advocated that as many > people as possible use stdint.h.There is this one by Paul Hsieh <http://www.azillionmonkeys.com/qed/pstdint.h>. It is not public domain, but seems to have a very permissive copyright. -- John Devereux
Reply by ●October 11, 20072007-10-11
Everett M. Greene wrote:> Does anyone have a public domain copy of stdint.h?Impossible. The interface provided by <stdint.h> is standardized --- but it's actual content isn't, and can't be. If portable code could define uint32_t itself, there would be no point for <stdint.h> to exist. This header *has* to be either provided by the compiler writer, or tailored to a particular compiler by an informed programmer.> Any number of people speak of rolling their own, but that seems a > little silly if it's advocated that as many people as possible use > stdint.h.The point is that you should only roll your own if the compiler authors didn't provide on. But if you *are* going to roll your own, it's a good idea to roll your own along the lines of the standard version, rather than inventing yet another set of my_u32 types. One of the problems this would avoid involves libraries from several independent vendors all used in the same project, who each thought they should roll their own type names for this, instead of following the C99 guidelines. The inevitable result is that two of them chose the *same* names, and now you get to resolve type redefinitions among other people's header files. Great fun --- not!
Reply by ●October 11, 20072007-10-11
David Brown wrote:> Hans-Bernhard Br�ker wrote:>> Not in this particular case. If the platform doesn't have 32-bit >> integers, it can't have 32-bit hardware registers, so it shouldn't >> compile this code.> It's perhaps more accurate to talk about the platform "supporting" > 32-bit integers - it's not clear whether you are trying to say that the > cpu in question must be at least 32 bits (which is of course not the > case - your code would work perfectly well on an 8-bit cpu, which > regularly have 16-bit hardware registers and perhaps also 32-bit > hardware registers) or just that it can work with 32-bit data (as > distinct from, say, a 36-bit machine).I meant exactly what I wrote. The question is: does it _have_ a 32-bit data type, or not? The rules on uint32_t are quite clear: it's provided if and only if the platform has an unsigned integer type exactly 32 bits wide. So if uint32_t isn't available, then there cannot possibly be 32-bit hardware registers for the up-thread code sample to use, because there's no type of the right width to work with them. Thus it should fail, and it'd preferrably fail as loudly as it can. I.e. it shouldn't even compile.
Reply by ●October 11, 20072007-10-11
On Thu, 11 Oct 2007 21:27:39 +0200, Hans-Bernhard Br�ker <HBBroeker@t-online.de> wrote:>David Brown wrote: >> Hans-Bernhard Br�ker wrote: > >>> Not in this particular case. If the platform doesn't have 32-bit >>> integers, it can't have 32-bit hardware registers, so it shouldn't >>> compile this code. > >> It's perhaps more accurate to talk about the platform "supporting" >> 32-bit integers - it's not clear whether you are trying to say that the >> cpu in question must be at least 32 bits (which is of course not the >> case - your code would work perfectly well on an 8-bit cpu, which >> regularly have 16-bit hardware registers and perhaps also 32-bit >> hardware registers) or just that it can work with 32-bit data (as >> distinct from, say, a 36-bit machine). > >I meant exactly what I wrote. The question is: does it _have_ a 32-bit >data type, or not? The rules on uint32_t are quite clear: it's provided >if and only if the platform has an unsigned integer type exactly 32 bits >wide. > >So if uint32_t isn't available, then there cannot possibly be 32-bit >hardware registers for the up-thread code sample to use, because there's >no type of the right width to work with them. Thus it should fail, and >it'd preferrably fail as loudly as it can. I.e. it shouldn't even compile.While any hardware platform should be able to support uint32_t, the question is really how to support "volatile uint32_t" in all cases, since the C language specification does not specify how to handle interrupt disabling. Paul
Reply by ●October 12, 20072007-10-12
Paul Keinanen wrote:> On Thu, 11 Oct 2007 21:27:39 +0200, Hans-Bernhard Br�ker > <HBBroeker@t-online.de> wrote: > >> David Brown wrote: >>> Hans-Bernhard Br�ker wrote: >>>> Not in this particular case. If the platform doesn't have 32-bit >>>> integers, it can't have 32-bit hardware registers, so it shouldn't >>>> compile this code. >>> It's perhaps more accurate to talk about the platform "supporting" >>> 32-bit integers - it's not clear whether you are trying to say that the >>> cpu in question must be at least 32 bits (which is of course not the >>> case - your code would work perfectly well on an 8-bit cpu, which >>> regularly have 16-bit hardware registers and perhaps also 32-bit >>> hardware registers) or just that it can work with 32-bit data (as >>> distinct from, say, a 36-bit machine). >> I meant exactly what I wrote. The question is: does it _have_ a 32-bit >> data type, or not? The rules on uint32_t are quite clear: it's provided >> if and only if the platform has an unsigned integer type exactly 32 bits >> wide. >> >> So if uint32_t isn't available, then there cannot possibly be 32-bit >> hardware registers for the up-thread code sample to use, because there's >> no type of the right width to work with them. Thus it should fail, and >> it'd preferrably fail as loudly as it can. I.e. it shouldn't even compile. >I thought that was what you meant, but was not entirely sure.> While any hardware platform should be able to support uint32_t, the > question is really how to support "volatile uint32_t" in all cases, > since the C language specification does not specify how to handle > interrupt disabling. >"volatile" has absolutely nothing to do with atomic access! This is not an uncommon mistake, but it is a misunderstanding that can lead to very subtle problems that are hard to diagnose (that's the case with most atomic access errors). C has no way to describe an atomic access - all "volatile" gives you is a promise to read from or write to the memory system when asked, and with restrictions on re-ordering. It says nothing about atomic access, and nothing about cache issues or memory writeback buffers (another common misconception about "volatile", relevant to bigger cpus).