EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Delay Routine: Fully-portable C89 if possible

Started by Martin Wells October 9, 2007
Hans-Bernhard Br&ouml;ker <HBBroeker@t-online.de> writes:

> John Devereux wrote: > >> OK. I think I do see the rationale, but I don't find it convincing >> enough to want to expunge the native types and "pollute" my code with >> *int*_t everywhere. > > Who said anything about expunging native types? The reason for > uint16_t and friends is that there situations (e.g. if you want to > control wrap-around behaviour in an expression) where you need a type > of exactly that size, or the code won't work.
Well that's fine, although I am still having trouble imagining an actual example, from the perspective of my own work.
> Now, of course "unsigned int" might work just the same, too --- on > the platform the code is aimed at now. But the next controller you > want to run it on may be a 32-bit one. So you'll have to go over > the *entire* code and decide, based on design documentation (lots of > it, hopefully), comments and the occasional guess, which of those > "unsigned int" was actually meant to be 16-bit unsigned, sharp, and > which wasn't. Better to spell it out right there on the first shot, > and be done with it.
OK - although I know there are others advocating the exclusive use of these types.
> Ultimately, the closer you look, the less useful the traditional, > "native" integer types turn out to be. > >> One other point I did not mention (perhaps someone else did?) would be >> interfacing to standard C functions. E.g. What happens when you call >> printf with a uint32_t, or a int_atleast_32_and_fast_please_t? > > You learn about <inttypes.h>. And you make sure the tools (compiler, > lint) are up to the task of helping you with these like they hopefully > already do with the traditional integer types. The ability to have > tools help you with these is actually a major reason why they should > be standardized. Lint shouldn't have to learn everybody's and their > grandma's private re-invention of uint16_t.
-- John Devereux
On Wed, 10 Oct 2007 09:09:43 +0200, David Brown wrote:

> First the simple part - omitting the "int" part of declarations and > definitions is an abomination
That is a matter of personal taste.
> brought about by the terrible keyboards > K&R had to work with when developing C.
Dunno, maybe, but it is not relevant.
> The type in question is called > "long unsigned int" - "long" and "unsigned" are type qualifiers.
No. Unless the C-standard has changed overnight this is not true. per section 6.7.2 of the standard all of "long", "unsigned" and "int" are called 'type-specifier' in the standard. A list of valid _sets_ of type- specifiers is presented in that section and it includes "long" and "long int" as well "unsigned" and "unsigned int", etc. per section 6.7.3 'type-qualifier' is defined as one of "const", "restrict" or "volatile" and nothing else. Rob
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? -- Chuck F (cbfalconer at maineline dot net) Available for consulting/temporary embedded and systems. <http://cbfalconer.home.att.net> -- Posted via a free Usenet account from http://www.teranews.com
David Brown wrote:
>
... snip ...
> > On the other hand, if I were writing a C program on a PC or an > embedded Linux box, I'd expect to use the fundamental C types a > lot more often - because then parameters like the size of an "int" > are known and fixed, "int" is generally the fastest type (which is > not always the case in embedded systems), and there are far fewer > demands in trying to use the smallest possible type in order to > save memory.
Don't assume that gcc will check. It will if the format string is a known constant. It can't (in practice) if the format string is a variable. I.e.: int doprint(char *s1, char *s2) { return printf(s1, s2); } will do no checking. -- Chuck F (cbfalconer at maineline dot net) Available for consulting/temporary embedded and systems. <http://cbfalconer.home.att.net> -- Posted via a free Usenet account from http://www.teranews.com
In article <w9HGTEGLBLDHFAbN@phaedsys.demon.co.uk>, Chris Hills says...
> In message <470b8d0c$0$3221$8404b019@news.wineasy.se>, David Brown > <david@westcontrol.removethisbit.com> writes > >As for using floating point ops for a delay - that's a bad idea too. > > Yes > > I would suggest a HW timer and an interrupt.
That might be overkill for a simple delay timer. I don't like using a dummy loop for delays (too fiddly if noting else) but a simple poll on a free running timer should be sufficient for a delay. Robert -- Posted via a free Usenet account from http://www.teranews.com
In article <470D43DD.CD7CC0EB@yahoo.com>, 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. --Tom.
CBFalconer wrote:
> David Brown wrote: > ... snip ... >> On the other hand, if I were writing a C program on a PC or an >> embedded Linux box, I'd expect to use the fundamental C types a >> lot more often - because then parameters like the size of an "int" >> are known and fixed, "int" is generally the fastest type (which is >> not always the case in embedded systems), and there are far fewer >> demands in trying to use the smallest possible type in order to >> save memory. > > Don't assume that gcc will check. It will if the format string is > a known constant. It can't (in practice) if the format string is a > variable. I.e.: > > int doprint(char *s1, char *s2) { > return printf(s1, s2); > } > > will do no checking. >
That's entirely correct. But in the few applications in which I use printf (or rather, snprintf), I use a fixed format string. If you are using a variable as the format string, then gcc can't help you with compile-time checks. gcc's format checking is not magical - but it's pretty good nonetheless. You can even misuse it as a way of type-checking other variable length parameter functions (if you feel such functions have any place in reliable programming). mvh., David
"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.
John Devereux wrote:
> Hans-Bernhard Br&ouml;ker <HBBroeker@t-online.de> writes: > >> John Devereux wrote: >> >>> OK. I think I do see the rationale, but I don't find it convincing >>> enough to want to expunge the native types and "pollute" my code with >>> *int*_t everywhere. >> Who said anything about expunging native types? The reason for >> uint16_t and friends is that there situations (e.g. if you want to >> control wrap-around behaviour in an expression) where you need a type >> of exactly that size, or the code won't work. > > Well that's fine, although I am still having trouble imagining an > actual example, from the perspective of my own work. > >> Now, of course "unsigned int" might work just the same, too --- on >> the platform the code is aimed at now. But the next controller you >> want to run it on may be a 32-bit one. So you'll have to go over >> the *entire* code and decide, based on design documentation (lots of >> it, hopefully), comments and the occasional guess, which of those >> "unsigned int" was actually meant to be 16-bit unsigned, sharp, and >> which wasn't. Better to spell it out right there on the first shot, >> and be done with it. > > OK - although I know there are others advocating the exclusive use of > these types. >
I'm a great believer in the right tool for the job. If your programming is exclusively on 32-bit systems, then you can safely assume that "int" is 32-bit - that suits your code, and the work you are doing. For others, that may not be the case. Some people find that using "uint32_t" style types exclusively is best for *their* programming, while others like a mixture (size-specific for interfaces, "native" for things like minor local variables), and others like to use a range of application-specific types (such as one would with Ada). And in this particular thread, the O/P wanted safe portable code, which implies the need for size-specific types.
>> Ultimately, the closer you look, the less useful the traditional, >> "native" integer types turn out to be. >> >>> One other point I did not mention (perhaps someone else did?) would be >>> interfacing to standard C functions. E.g. What happens when you call >>> printf with a uint32_t, or a int_atleast_32_and_fast_please_t? >> You learn about <inttypes.h>. And you make sure the tools (compiler, >> lint) are up to the task of helping you with these like they hopefully >> already do with the traditional integer types. The ability to have >> tools help you with these is actually a major reason why they should >> be standardized. Lint shouldn't have to learn everybody's and their >> grandma's private re-invention of uint16_t. >
rob windgassen wrote:
> On Wed, 10 Oct 2007 09:09:43 +0200, David Brown wrote: > >> First the simple part - omitting the "int" part of declarations and >> definitions is an abomination > > That is a matter of personal taste. >
In the specific case of type names like "unsigned int", it is arguably a matter of taste - although it is a general rule of programming style that it is better to be explicit than implicit. "long", for example, can be used for both "long int" and "long double" - there is no type "long" in itself. Other implicit "int" cases in C have been gradually dropped in newer standards. For example, it used to be legal to declare a function: extern foo(); which returns an "int", and which takes as many "int" parameters as you want. This sort of thing is not a matter of taste - it is simply bad programming style, and contrary to every attempt at safe programming. If the proposed newer C types _Fract and _Accum become a reality, then there will be an even stronger reason to drop the implicit "int", since these will also support specified types like "unsigned short _Fract".
>> brought about by the terrible keyboards >> K&R had to work with when developing C. > > Dunno, maybe, but it is not relevant. >
On closer checking, it seems that the implicit "int" comes from the language "B", but the basic reason was to minimise keystrokes (that was also the reason for allowing functions without declarations, and other such implicit or abbreviated coding).
>> The type in question is called >> "long unsigned int" - "long" and "unsigned" are type qualifiers. > > No. Unless the C-standard has changed overnight this is not true. > > per section 6.7.2 of the standard all of "long", "unsigned" and "int" are > called 'type-specifier' in the standard. A list of valid _sets_ of type- > specifiers is presented in that section and it includes "long" and "long > int" as well "unsigned" and "unsigned int", etc. > per section 6.7.3 'type-qualifier' is defined as one of "const", > "restrict" or "volatile" and nothing else. >
You are correct here regarding the official C terminology - "long", etc., are "type specifiers". But they do not in themselves form types - the "int" is implied if it is missing, but it is still part of the type.
> Rob >

Memfault Beyond the Launch