Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #71431 > unrolled thread
| Started by | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| First post | 2015-09-29 08:55 -0700 |
| Last post | 2015-10-14 17:00 +0200 |
| Articles | 20 on this page of 42 — 8 participants |
Back to article view | Back to comp.lang.c
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: Spurious diagnostic messages Tim Rentsch <txr@alumni.caltech.edu> - 2015-09-29 08:55 -0700
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-09-29 10:22 -0700
Re: Spurious diagnostic messages Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-04 23:42 -0700
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-05 09:41 -0700
Re: Spurious diagnostic messages Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-09 13:22 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-09-29 22:28 +0200
Re: Spurious diagnostic messages Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-05 00:14 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-05 10:30 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-05 10:06 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-06 09:23 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-06 11:18 -0700
Re: Spurious diagnostic messages Keith Thompson <kst-u@mib.org> - 2015-10-06 14:29 -0700
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-06 15:33 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-07 12:35 +0200
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-07 10:57 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-07 07:09 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-08 09:28 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-08 01:45 -0700
Re: Spurious diagnostic messages glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-10-08 09:42 +0000
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-08 09:42 -0700
Re: Spurious diagnostic messages Keith Thompson <kst-u@mib.org> - 2015-10-07 09:40 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-08 09:44 +0200
Re: Spurious diagnostic messages Keith Thompson <kst-u@mib.org> - 2015-10-08 08:25 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-08 20:36 +0200
Re: Spurious diagnostic messages Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-10-08 13:28 -0700
Re: Spurious diagnostic messages Bartc <bc@freeuk.com> - 2015-10-08 22:21 +0100
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-08 14:33 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-09 10:24 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-09 08:35 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-11 15:18 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-11 14:53 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-12 09:17 +0200
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-12 00:37 -0700
Re: Spurious diagnostic messages Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-10-10 00:31 -0700
Re: Spurious diagnostic messages Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-10 10:50 +0100
Re: Spurious diagnostic messages Bartc <bc@freeuk.com> - 2015-10-10 11:01 +0100
Re: Spurious diagnostic messages Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-10-10 03:43 -0700
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-10 13:26 -0700
Re: Spurious diagnostic messages supercat@casperkitty.com - 2015-10-10 13:34 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-11 15:25 +0200
Re: Spurious diagnostic messages Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-14 07:43 -0700
Re: Spurious diagnostic messages David Brown <david.brown@hesbynett.no> - 2015-10-14 17:00 +0200
Page 1 of 3 [1] 2 3 Next page →
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-09-29 08:55 -0700 |
| Subject | Re: Spurious diagnostic messages |
| Message-ID | <kfnlhbpcj9a.fsf@x-alumni2.alumni.caltech.edu> |
David Brown <david.brown@hesbynett.no> writes: > On 24/08/15 18:58, Tim Rentsch wrote: > >> [snipping almost everything as there is only one point >> I am responding to.] > binary literals are solidly established in compliers and > we can use them when we find them helpful regardless of the C > standards. And I have no problem with that. If a group or subcommunity thinks a particular extension is useful and wants to use it for their own development, I wouldn't try to stop them. But that isn't in and of itself a compelling argument to change the ISO C standard.
[toc] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-09-29 10:22 -0700 |
| Message-ID | <dd0a1ad4-6842-4b3d-ada8-eaad56a0b2c3@googlegroups.com> |
| In reply to | #71431 |
On Tuesday, September 29, 2015 at 10:56:02 AM UTC-5, Tim Rentsch wrote: > And I have no problem with that. If a group or subcommunity > thinks a particular extension is useful and wants to use it > for their own development, I wouldn't try to stop them. But > that isn't in and of itself a compelling argument to change > the ISO C standard. If some particular syntax has any useful whatsoever, I would suggest that it should be in the Standard unless one of the following conditions applies (note that for many forms of syntax of marginal utility, at least one *will* apply): 1. There might plausibly be cases where it would be more useful to assign some other meaning to the syntax. 2. The total amount of effort required to add the feature to all compilers where people might wish to use it exceeds the total value that could be obtained through use of the feature. If one is going to modify a compiler to add more advanced language features, adding binary literals as well would seem to require a relatively slight amount of effort. Further, even if one didn't want to go so far as to mandate that all compilers must support such a feature, it would still be possible to have it be part of a language merely by adding a requirement that any compiler which defines __BINARY_LITERAL_SUPPORT as yielding a non-zero value must interpret tokens starting with "0b" as binary literals. IMHO, the usefulness of the C Family of Dialects would be greatly enhanced if the Standards Committee were to officially acknowledge the existence of extensions, and catalog their behaviors. One of the reasons the C Family of Dialects thrived starting in the late 1980s is that compiler writers not only regarded the inclusion of useful features not mandated by the Standard as a desirable thing, but in many cases they also strove for compatibility with extensions provided by their competitors' compilers. If everyone had taken the attitude that nobody should ever be encouraged to write anything other than Strictly-Conforming programs, C would have been pretty much dead by 1990.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-04 23:42 -0700 |
| Message-ID | <kfnpp0t4y05.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #71442 |
supercat@casperkitty.com writes: > On Tuesday, September 29, 2015 at 10:56:02 AM UTC-5, Tim Rentsch wrote: >> And I have no problem with that. If a group or subcommunity >> thinks a particular extension is useful and wants to use it >> for their own development, I wouldn't try to stop them. But >> that isn't in and of itself a compelling argument to change >> the ISO C standard. > > If some particular syntax has any useful whatsoever, I would > suggest that it should be in the Standard unless one of the > following conditions applies (note that for many forms of syntax > of marginal utility, at least one *will* apply): > > 1. There might plausibly be cases where it would be more useful to > assign some other meaning to the syntax. > > 2. The total amount of effort required to add the feature to all > compilers where people might wish to use it exceeds the total > value that could be obtained through use of the feature. > > [other related comments] I find it hard to take suggestions this extreme seriously.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-05 09:41 -0700 |
| Message-ID | <27712b5f-3ddf-4323-8dc2-82689c28f9f9@googlegroups.com> |
| In reply to | #71946 |
On Monday, October 5, 2015 at 1:42:45 AM UTC-5, Tim Rentsch wrote: > supercat writes: > > If some particular syntax has any useful whatsoever, I would > > suggest that it should be in the Standard unless one of the > > following conditions applies (note that for many forms of syntax > > of marginal utility, at least one *will* apply): > > > > 1. There might plausibly be cases where it would be more useful to > > assign some other meaning to the syntax. > > > > 2. The total amount of effort required to add the feature to all > > compilers where people might wish to use it exceeds the total > > value that could be obtained through use of the feature. > > > > [other related comments] > > I find it hard to take suggestions this extreme seriously. Did you see my note about how for most prospective features at least one of the reasons for *not* including it would apply? The idea that a feature should be added to the language if the syntax would be unlikely to conflict with any other features anyone else might want to use for some other purpose, and if the value added by adding the feature exceeds the total costs imposed by adding it, doesn't seem "extreme". Do you consider that statement extreme, or did you not recognize my earlier statement as equivalent? If features which meet both of the above criteria should be added, then any refusal to add a feature should justifiable on the basis that it fails to meet one of the criteria. Since adding any feature should have some cost, a feature must have at least some demonstrated value to be worth adding. The lower the cost to add a feature, however, the lower the value that would be necessary to justify that cost.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-09 13:22 -0700 |
| Message-ID | <kfn1td33i7o.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #71986 |
supercat@casperkitty.com writes: > On Monday, October 5, 2015 at 1:42:45 AM UTC-5, Tim Rentsch wrote: >> supercat writes: >>> If some particular syntax has any useful whatsoever, I would >>> suggest that it should be in the Standard unless one of the >>> following conditions applies (note that for many forms of syntax >>> of marginal utility, at least one *will* apply): >>> >>> 1. There might plausibly be cases where it would be more useful to >>> assign some other meaning to the syntax. >>> >>> 2. The total amount of effort required to add the feature to all >>> compilers where people might wish to use it exceeds the total >>> value that could be obtained through use of the feature. >>> >>> [other related comments] >> >> I find it hard to take suggestions this extreme seriously. > > Did you see my note about how for most prospective features at least one > of the reasons for *not* including it would apply? Yes.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-09-29 22:28 +0200 |
| Message-ID | <mues65$n69$1@dont-email.me> |
| In reply to | #71431 |
On 29/09/15 17:55, Tim Rentsch wrote: > David Brown <david.brown@hesbynett.no> writes: > >> On 24/08/15 18:58, Tim Rentsch wrote: >> >>> [snipping almost everything as there is only one point >>> I am responding to.] > >> binary literals are solidly established in compliers and >> we can use them when we find them helpful regardless of the C >> standards. > > And I have no problem with that. If a group or subcommunity > thinks a particular extension is useful and wants to use it > for their own development, I wouldn't try to stop them. But > that isn't in and of itself a compelling argument to change > the ISO C standard. > With C11, the C standard introduced the idea of optional features. That I think should lower the bar to adding new features to C - it means that a feature can be added to the standard without requiring compilers to implement it. This thread has been going on for a while, but if I may try to sum up: On the one side, we have: 0b binary literals are a common extension that are implemented on a number of compilers. On those compilers, making it part of the standard is almost zero cost. 0b binary literals are a useful feature, and many embedded developers take advantage of them where they exist. 0b binary literals are part of the current C++ standards. 0b binary literals do not conflict with existing C standards in any way. On the other side, we have: Binary literals are not essential - it is possible to use hex literals or macros (albeit somewhat ugly and complicated) to make binary constants. Binary literals are not used very much, even by embedded programmers. Binary literals are of very little potential use to C programmers other than embedded programmers (though embedded programming is an increasing proportion of C usage). Am I missing anything? I believe that C should adopt them in future standards - I believe the C standards need to keep abreast with real-world usage, especially in the increasingly important embedded area, or they risk being left behind as compiler implementers make their own "standards". Such fragmentation would not be good for anyone. (In this case, most but not all compilers have gone for the same format for binary literals.) I would be quite happy with the idea of a subcommittee producing an extension to the C standards to cover features such as this that would suit embedded programming - as long as it is an "official" standard with a similar weight to the main C standards. A standard such as this could include many additions, refinements, restrictions or enhancements to the main C standards that for some reason are undesirable in the main standard.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-05 00:14 -0700 |
| Message-ID | <kfnlhbh4wjj.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #71447 |
David Brown <david.brown@hesbynett.no> writes: > On 29/09/15 17:55, Tim Rentsch wrote: >> David Brown <david.brown@hesbynett.no> writes: >> >>> On 24/08/15 18:58, Tim Rentsch wrote: >>> >>>> [snipping almost everything as there is only one point >>>> I am responding to.] >>> >>> binary literals are solidly established in compliers and >>> we can use them when we find them helpful regardless of the C >>> standards. >> >> And I have no problem with that. If a group or subcommunity >> thinks a particular extension is useful and wants to use it >> for their own development, I wouldn't try to stop them. But >> that isn't in and of itself a compelling argument to change >> the ISO C standard. > > With C11, the C standard introduced the idea of optional features. > That I think should lower the bar to adding new features to C - it > means that a feature can be added to the standard without requiring > compilers to implement it. The downside of optional features is that they tend to fragment the language and they may die from neglect due to self-fulfilling prophecy. I understand why some parts of C99 were make optional in C11, but IMO such a step should be taken only very sparingly. > This thread has been going on for a while, but if I may try to sum up: > > On the one side, we have: > > 0b binary literals are a common extension that are implemented on a > number of compilers. On those compilers, making it part of the > standard is almost zero cost. > > 0b binary literals are a useful feature, and many embedded developers > take advantage of them where they exist. > > 0b binary literals are part of the current C++ standards. > > 0b binary literals do not conflict with existing C standards in any way. > > On the other side, we have: > > Binary literals are not essential - it is possible to use hex literals > or macros (albeit somewhat ugly and complicated) to make binary > constants. > > Binary literals are not used very much, even by embedded programmers. > > Binary literals are of very little potential use to C programmers > other than embedded programmers (though embedded programming is an > increasing proportion of C usage). > > Am I missing anything? Yes, I would say the key point I've been trying to make all along is that the onus of presenting a convincing rationale rests on those who are advocating the new feature. I don't mind being convinced that binary constants are a worthwhile addition, but the basis for such a rationale should be more than just statements of opinion. > I believe that C should adopt them in future standards - I believe > the C standards need to keep abreast with real-world usage, > especially in the increasingly important embedded area, or they > risk being left behind as compiler implementers make their own > "standards". Such fragmentation would not be good for anyone. > (In this case, most but not all compilers have gone for the same > format for binary literals.) And that could be a good argument to make, except that it should be based on some sort of objective evidence, not just what person X or person Y thinks is true. > I would be quite happy with the idea of a subcommittee producing > an extension to the C standards to cover features such as this > that would suit embedded programming - as long as it is an > "official" standard with a similar weight to the main C standards. > A standard such as this could include many additions, refinements, > restrictions or enhancements to the main C standards that for some > reason are undesirable in the main standard. This path may be a more fruitful path to go down for what you want to do, both because the energy barrier is probably lower and because if there were such a subgroup consensus then that could be taken to compiler vendors for them to retrofit into existing C99 implementations. Whatever it is that ISO might end up doing about this question, one thing we can be sure of is that they will not make a retroactive change to previous versions of the standard.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-10-05 10:30 +0200 |
| Message-ID | <mutcah$tpt$1@dont-email.me> |
| In reply to | #71948 |
On 05/10/15 09:14, Tim Rentsch wrote: > David Brown <david.brown@hesbynett.no> writes: > >> On 29/09/15 17:55, Tim Rentsch wrote: >>> David Brown <david.brown@hesbynett.no> writes: >>> >>>> On 24/08/15 18:58, Tim Rentsch wrote: >>>> >>>>> [snipping almost everything as there is only one point >>>>> I am responding to.] >>>> >>>> binary literals are solidly established in compliers and >>>> we can use them when we find them helpful regardless of the C >>>> standards. >>> >>> And I have no problem with that. If a group or subcommunity >>> thinks a particular extension is useful and wants to use it >>> for their own development, I wouldn't try to stop them. But >>> that isn't in and of itself a compelling argument to change >>> the ISO C standard. >> >> With C11, the C standard introduced the idea of optional features. >> That I think should lower the bar to adding new features to C - it >> means that a feature can be added to the standard without requiring >> compilers to implement it. > > The downside of optional features is that they tend to fragment > the language and they may die from neglect due to self-fulfilling > prophecy. I understand why some parts of C99 were make optional > in C11, but IMO such a step should be taken only very sparingly. I agree that optional features can lead to fragmentation. But an even faster path to fragmentation is when the C standards ignore features that are implemented by a number of compilers, and used by at least a proportion of developers. This is the case with 0b binary literals. It is an example of a feature that has already fragmented the C compiler community. Standardising them as an optional feature would take control of that fragmentation. (Remember, with 0b binary literals, we are not talking about a new feature under consideration - it is a feature that exists already and is in common use in the most widely used C compilers.) > >> This thread has been going on for a while, but if I may try to sum up: >> >> On the one side, we have: >> >> 0b binary literals are a common extension that are implemented on a >> number of compilers. On those compilers, making it part of the >> standard is almost zero cost. >> >> 0b binary literals are a useful feature, and many embedded developers >> take advantage of them where they exist. >> >> 0b binary literals are part of the current C++ standards. >> >> 0b binary literals do not conflict with existing C standards in any way. >> >> On the other side, we have: >> >> Binary literals are not essential - it is possible to use hex literals >> or macros (albeit somewhat ugly and complicated) to make binary >> constants. >> >> Binary literals are not used very much, even by embedded programmers. >> >> Binary literals are of very little potential use to C programmers >> other than embedded programmers (though embedded programming is an >> increasing proportion of C usage). >> >> Am I missing anything? > > Yes, I would say the key point I've been trying to make all along is > that the onus of presenting a convincing rationale rests on those > who are advocating the new feature. I don't mind being convinced > that binary constants are a worthwhile addition, but the basis for > such a rationale should be more than just statements of opinion. You keep repeating that. As Ian has said, you appear to be asking for a level of evidence that is way beyond something that can be realistically presented (and certainly not something that can be given in a newsgroup), and that is well beyond the evidence that has been required in the past to justify far more significant and intrusive changes to the C language. Developers wanted 0b binary literals. Compiler developers implemented them. Developers use them. Embedded developers are happy with them from a practical viewpoint, but those of us who feel the standards are important and who want to avoid fragmentation would like them to be codified in the standards. This is all fact, not opinion. No one has given you numbers here, because no one knows any good numbers to give you - thus judgements about how many people benefit from binary literals, and how useful they are, are opinion. It's that simple. You will get no more evidence than this. If you really want a detailed cost-benefit analysis based on statistics of hours saved or bugs prevented in a fair comparison between C with binary literals and C without binary literals, you will need to conduct the research yourself. No one else is going to spend the millions needed to achieve this in a statistically valid manner - just as no one has spend the millions needed to get similar evidence before introducing VLAs or removing implicit ints. You set the bar too high here. And if your requirements match the attitude of the C standards committee, then they too set the bar too high - and they will sit in their ivory tower, watching as C becomes more fragmented and less and less relevant as a programming language. > >> I believe that C should adopt them in future standards - I believe >> the C standards need to keep abreast with real-world usage, >> especially in the increasingly important embedded area, or they >> risk being left behind as compiler implementers make their own >> "standards". Such fragmentation would not be good for anyone. >> (In this case, most but not all compilers have gone for the same >> format for binary literals.) > > And that could be a good argument to make, except that it should > be based on some sort of objective evidence, not just what person > X or person Y thinks is true. > >> I would be quite happy with the idea of a subcommittee producing >> an extension to the C standards to cover features such as this >> that would suit embedded programming - as long as it is an >> "official" standard with a similar weight to the main C standards. >> A standard such as this could include many additions, refinements, >> restrictions or enhancements to the main C standards that for some >> reason are undesirable in the main standard. > > This path may be a more fruitful path to go down for what you > want to do, both because the energy barrier is probably lower and > because if there were such a subgroup consensus then that could > be taken to compiler vendors for them to retrofit into existing > C99 implementations. Whatever it is that ISO might end up doing > about this question, one thing we can be sure of is that they > will not make a retroactive change to previous versions of the > standard. > (The committee have arguably already made retroactive changes to existing standards, by making parts of C99 optional in C11. But my interest here is for future standards, not old ones.)
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-05 10:06 -0700 |
| Message-ID | <929a5929-0a7e-4c01-9694-b2ee885c921f@googlegroups.com> |
| In reply to | #71953 |
On Monday, October 5, 2015 at 3:30:16 AM UTC-5, David Brown wrote: > I agree that optional features can lead to fragmentation. But an even > faster path to fragmentation is when the C standards ignore features > that are implemented by a number of compilers, and used by at least a > proportion of developers. Brilliantly said. I would further suggest that there are other aspects of the language where the principle applies even more strongly. Some people seem to have an attitude that C programmers should in most cases try to write code that would be guaranteed to work on any standards-conforming compiler for any architecture, no matter how obscure. I would posit that such an attitude is unrealistic and harmful. I have no problem with the concept that even if many programs would benefit from some feature or guarantee, there is usefulness to being able to use C on platforms which cannot provide or uphold that feature or guarantee, for the purpose of using programs that do not require it. I would counter, however, by saying that even if some platforms are not able to provide or honor a feature or guarantee which can be provided or honored by many platforms, that should not preclude the use of such features or guarantees within programs that will never need to run on platforms that can't provide or honor those features/guarantees. In the case of binary literals, I'm not sure I can see any reason why any implementation which could handle the other more complicated new features which have been added to the language would have any trouble handling binary literals. If one were trying to make a compiler run on a 48K machine even 100 bytes of extra code to support binary literals could be a problem. I don't think a C11-compiler would have any chance of running on a 48K machine with or without binary literals, however, and the likelihood that support for binary literals would stress an otherwise-usable compiler to the breaking point seems negligible.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-10-06 09:23 +0200 |
| Message-ID | <muvspj$svk$1@dont-email.me> |
| In reply to | #71988 |
On 05/10/15 19:06, supercat@casperkitty.com wrote: > On Monday, October 5, 2015 at 3:30:16 AM UTC-5, David Brown wrote: >> I agree that optional features can lead to fragmentation. But an even >> faster path to fragmentation is when the C standards ignore features >> that are implemented by a number of compilers, and used by at least a >> proportion of developers. > > Brilliantly said. I would further suggest that there are other aspects of > the language where the principle applies even more strongly. Some people > seem to have an attitude that C programmers should in most cases try to > write code that would be guaranteed to work on any standards-conforming > compiler for any architecture, no matter how obscure. I would posit that > such an attitude is unrealistic and harmful. While I obviously agree with the first part of that paragraph, I am not so sure about the rest. There are many types of C programmers, and they work in many different ways for different end-uses. There are people who write code that will be used on a wide variety of systems - often they don't know what those will be while they are writing their code. Sticking to the standards and avoiding any extensions or implementation-defined behaviour (and perhaps even avoiding any standard behaviour that they know is not implemented in some common compilers - such as C11), is vital to such work. And there are others who know /exactly/ what tools will be in use - when I do an update on a 10-year old project, I do so using precisely the same tools as I did 10 years ago. Most programmers will lie somewhere between those extremes. > > I have no problem with the concept that even if many programs would benefit > from some feature or guarantee, there is usefulness to being able to use > C on platforms which cannot provide or uphold that feature or guarantee, > for the purpose of using programs that do not require it. > > I would counter, however, by saying that even if some platforms are not > able to provide or honor a feature or guarantee which can be provided or > honored by many platforms, that should not preclude the use of such > features or guarantees within programs that will never need to run on > platforms that can't provide or honor those features/guarantees. I agree on that. If the compiler I am using for a project provides useful extensions, I will use them when they make a difference. Some of my code is reused between projects with different tools - for such code, unusual extensions should be avoided (or hidden behind macros). But much of it is either project specific, or will be re-used in projects with similar tools - extensions then make sense. As a small systems (bare bones) embedded programmer, my programs /always/ need at least some sort of extensions or tool-specific code for hardware interaction, embedded assembly, interrupt handling, etc. But I make a point of avoiding relying on implementation dependant behaviour for "normal" C code. Even if I know the code I am writing will only ever be run on this particular 16-bit microcontroller, I'll use int16_t rather than int when I want a 16-bit integer. I always know if my target is little-endian or big-endian - but any code that depends on that difference will be minimised, and well commented. > > In the case of binary literals, I'm not sure I can see any reason why any > implementation which could handle the other more complicated new features > which have been added to the language would have any trouble handling > binary literals. If one were trying to make a compiler run on a 48K machine > even 100 bytes of extra code to support binary literals could be a problem. > I don't think a C11-compiler would have any chance of running on a 48K > machine with or without binary literals, however, and the likelihood that > support for binary literals would stress an otherwise-usable compiler to > the breaking point seems negligible. > Binary literals are already implemented on most compilers targeting embedded systems. In most cases, they use the 0b format - but there are some exceptions for toolchains that had implemented binary literals before 0b became the de facto standard. However, these are mostly for smaller and more painful microcontrollers, and code written for such devices is typically very highly specialised to the microcontroller and compiler.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-06 11:18 -0700 |
| Message-ID | <f12f739d-5b79-41fc-b37b-96c0c1cd4aac@googlegroups.com> |
| In reply to | #72029 |
On Tuesday, October 6, 2015 at 2:23:41 AM UTC-5, David Brown wrote: > On 05/10/15 19:06, supercat wrote: > > Brilliantly said. I would further suggest that there are other aspects of > > the language where the principle applies even more strongly. Some people > > seem to have an attitude that C programmers should in most cases try to > > write code that would be guaranteed to work on any standards-conforming > > compiler for any architecture, no matter how obscure. I would posit that > > such an attitude is unrealistic and harmful. > > While I obviously agree with the first part of that paragraph, I am not > so sure about the rest. There are many types of C programmers, and they > work in many different ways for different end-uses. There are people > who write code that will be used on a wide variety of systems - often > they don't know what those will be while they are writing their code. > Sticking to the standards and avoiding any extensions or > implementation-defined behaviour (and perhaps even avoiding any standard > behaviour that they know is not implemented in some common compilers - > such as C11), is vital to such work. And there are others who know > /exactly/ what tools will be in use - when I do an update on a 10-year > old project, I do so using precisely the same tools as I did 10 years > ago. Most programmers will lie somewhere between those extremes. My philosophy over the years has been that it is reasonable for most kinds of code to assume: 1. It will never be called upon to run on any platform which does not have non-padded two's-complement and unsigned 16-bit and 32-bit integer types available which are stored either big-endian or little-endian format, but code should not make assumptions about *which* absent a good reason to do so. 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing to standard names for 16-bit and 32-bit types were "short" and "long". 3. Writing to an object of a particular type is equivalent to overwriting the underlying "char" values with that type's "char[]" representation, except that types which contain padding may arbitrarily decide to copy the padding or leave it alone. 4. Reading an object of a particular type is equivalent to reading the underlying "char" values and interpreting them as the type in question. 5. Comparisons between unrelated pointers abide by the rule that if char*p is a pointer to an object of size s, and char *q is any pointer, the expression (q>=p && q<p+s) will yield one whenever q identifies part of p, it will likely (though not necessarily 100% reliably) yield zero when q does not identify part of p, and will never do anything other than yield zero or one in any case. 6. In the absence of absented documentation to the contrary, arithmetic overflow on integer types will yield a Partially-Indeterminate Value with the semantics that every rvalue conversion will yield a number which is congruent to the arithmetically correct value modulo the range of the type, but might not necessarily be within the range of the type. If such a value is stored in a variable, repeated rvalue conversions may yield different values subject to that constraint. 7. The expression, x<<y, when y>=INTSIZE, will yield either x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion; right-shifts will behave likewise. 8. Left shifts of negative values will behave as multiplying by a power of two, with the same overflow semantics described in 6. 9. Right shifts of negative values will be performed using sign extension. I know of very few cases where where straightforwardly-generated code which targets a modern machine, would work in cases mandated by the C Standard, and would behave consistent with the above behaviors 1-9, would be any more expensive than the optimal straightforwardly-generated code which was not required to abide by 1-9, and I until recently the default behavior of every compiler I've used (and I've used quite a few, for a wide range of platforms) would be to uphold the above guarantees even in those rare cases where it might require an extra instruction or two (e.g. on processors without an arithmetic-right-shift instruction). IMHO, there is no good reason why the above behaviors should not be recognized as common extensions, which should be supported in the absence of a compelling reason to do otherwise. Being *explicitly* told by a programmer "this section of code does not require certain commonly-provided guarantees" would be a compelling reason. The fact that a particular hardware platform would make code which upheld a certain guarantee 50% slower than it would have to be otherwise could be a compelling reason. The theoretical possibility that it might be possible to make some code meet its behavioral obligations more quickly would not be such a compelling reason. > If the compiler I am using for a project provides useful extensions, I > will use them when they make a difference. Some of my code is reused > between projects with different tools - for such code, unusual > extensions should be avoided (or hidden behind macros). But much of it > is either project specific, or will be re-used in projects with similar > tools - extensions then make sense. As a small systems (bare bones) > embedded programmer, my programs /always/ need at least some sort of > extensions or tool-specific code for hardware interaction, embedded > assembly, interrupt handling, etc. Have you used any platforms which would have any difficulty meeting any of the above guarantees? Some platforms would be able to offer useful guarantees beyond the above (e.g. some will allow code to test for object overlap without any false positives, though e.g. some 8086 compilers will not). Code which relies upon the guarantees above can in many cases be clearer than any comparably-efficient code which does not (if comparable efficiency without those guarantees is even possible). While it may be useful to be able to use hardware that cannot efficiently offer those guarantees to run programs that don't require them, I see no reason to believe that future hardware will be any less capable of honoring them than present hardware. While I recognize that the value of assumptions about what things will or won't alias, the rules about type-punning are simultaneously insufficient to identify most cases where a compiler could safely and usefully assume things won't alias, but so restrictive as to preclude many useful programming techniques. > But I make a point of avoiding relying on implementation dependant > behaviour for "normal" C code. Even if I know the code I am writing > will only ever be run on this particular 16-bit microcontroller, I'll > use int16_t rather than int when I want a 16-bit integer. I always know > if my target is little-endian or big-endian - but any code that depends > on that difference will be minimised, and well commented. My point is that behaviors like those listed above should not be considered "implementation-dependent". C is the only language I know of in which the set of "safe" behaviors has decreased rather than increased over the last decade. > Binary literals are already implemented on most compilers targeting > embedded systems. In most cases, they use the 0b format - but there are > some exceptions for toolchains that had implemented binary literals > before 0b became the de facto standard. However, these are mostly for > smaller and more painful microcontrollers, and code written for such > devices is typically very highly specialised to the microcontroller and > compiler. Do you know of any situations in which different compilers that accept 0bXXX literals would not interpret them the same way, or can you think of any reason why a compiler would want to? If not, what disadvantages would you see to having a Standard indicate that implementations which define a certain macro (e.g. __STDC_BINARY_LITERALS) with a non-zero value are required to interpret literals of that form the way that existing implementations do? The only costs imposed by such a rule would be: 1. Compilers which implement such literals should add a predefined macro indicating that. 2. Compilers which don't implement such literals must refrain from using the identifier __STDC_BINARY_LITERALS for some other purpose. Cost #1 seems pretty small, and cost #2 negligible.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-06 14:29 -0700 |
| Message-ID | <ln37xnlm7l.fsf@kst-u.example.com> |
| In reply to | #72072 |
supercat@casperkitty.com writes:
[...]
> My philosophy over the years has been that it is reasonable for most kinds
> of code to assume:
>
> 1. It will never be called upon to run on any platform which does not have
> non-padded two's-complement and unsigned 16-bit and 32-bit integer types
> available which are stored either big-endian or little-endian format,
> but code should not make assumptions about *which* absent a good reason
> to do so.
Personally, I've worked on one platform that had no 16-bit integer
types, and another that had no 16-bit or 32-bit integer types. On both,
char was 8 bits and int was 64 bits. That was admittedly an unusual
case (Cray supercomputers), and I'm not necessarily saying you shouldn't
make that particular assumption. But it's not hard to imagine that
future systems might violate your assumption.
> 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing
> to standard names for 16-bit and 32-bit types were "short" and "long".
long is usually 64 bits on 64-bit systems (apparently MS Windows is an
exception). I'm not sure how common such systems were prior to 1999.
If I needed exact 16-bit and 32-bit types and I was stuck with a pre-C99
compiler, I'd probably use Doug Gwyn's q8 or something similar.
http://www.lysator.liu.se/c/q8/index.html
> 3. Writing to an object of a particular type is equivalent to overwriting
> the underlying "char" values with that type's "char[]" representation,
> except that types which contain padding may arbitrarily decide to copy
> the padding or leave it alone.
The standard defines a an "object representation" as a set of
unsigned char values; it can be obtained by memcpy(), for example.
I find that using unsigned char for raw byte values is conceptually
cleaner. I'm vanishingly unlike to have to use a system with signed
char using non-2's-complement with two reprentations for zero,
but by using unsigned char I don't have to think about it.
[...]
> 5. Comparisons between unrelated pointers abide by the rule that if char*p
> is a pointer to an object of size s, and char *q is any pointer,
> the expression (q>=p && q<p+s) will yield one whenever q identifies part
> of p, it will likely (though not necessarily 100% reliably) yield zero
> when q does not identify part of p, and will never do anything other
> than yield zero or one in any case.
That's probably a valid assumption for any system with a monolithic
memory addressing scheme. But I can imagine other schemes becoming more
popular in the unspecified future. A scheme where each allocated object
has its own disjoint address space could have some advantages.
[...]
> 7. The expression, x<<y, when y>=INTSIZE, will yield either
> x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion;
> right-shifts will behave likewise.
>
> 8. Left shifts of negative values will behave as multiplying by a power of
> two, with the same overflow semantics described in 6.
>
> 9. Right shifts of negative values will be performed using sign extension.
How difficult is it to avoid shifts whose behavior is not defined by the
standard? How useful is it be able to compute, for example, n<<32,
where n is of type uint32_t?
[...]
> My point is that behaviors like those listed above should not be considered
> "implementation-dependent". C is the only language I know of in which the
> set of "safe" behaviors has decreased rather than increased over the last
> decade.
Not, as far as I can tell, due to any changes in the language. (We've
discussed that at some length before.)
>> Binary literals are already implemented on most compilers targeting
>> embedded systems. In most cases, they use the 0b format - but there are
>> some exceptions for toolchains that had implemented binary literals
>> before 0b became the de facto standard. However, these are mostly for
>> smaller and more painful microcontrollers, and code written for such
>> devices is typically very highly specialised to the microcontroller and
>> compiler.
>
> Do you know of any situations in which different compilers that accept 0bXXX
> literals would not interpret them the same way, or can you think of any
> reason why a compiler would want to? If not, what disadvantages would you
> see to having a Standard indicate that implementations which define a
> certain macro (e.g. __STDC_BINARY_LITERALS) with a non-zero value are
> required to interpret literals of that form the way that existing
> implementations do? The only costs imposed by such a rule would be:
>
> 1. Compilers which implement such literals should add a predefined macro
> indicating that.
>
> 2. Compilers which don't implement such literals must refrain from using the
> identifier __STDC_BINARY_LITERALS for some other purpose.
>
> Cost #1 seems pretty small, and cost #2 negligible.
I personally would oppose making binary constants an optional
feature. It would IMHO be worse than not adding them to the
standard at all. The cost of implementing them is so small that
I see no point in permitting compilers *not* to implement them.
The value of __STDC_VERSION__ would reliably tell you whether
they're supported (assuming a non-buggy compiler).
The cost of defining and documenting __STDC_BINARY_LITERALS is minor
for compiler writers, but it would IMHO be a wart on the language
and confusing to users.
C99 added hexadecimal floating-point constants and made them
mandatory for all conforming compilers.
--
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 | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-06 15:33 -0700 |
| Message-ID | <ee7ea566-ae2a-4431-9f9a-a460ba753d8a@googlegroups.com> |
| In reply to | #72088 |
On Tuesday, October 6, 2015 at 4:30:01 PM UTC-5, Keith Thompson wrote:
> supercat writes:
> > 1. It will never be called upon to run on any platform which does not have
> > non-padded two's-complement and unsigned 16-bit and 32-bit integer types
> > available which are stored either big-endian or little-endian format,
> > but code should not make assumptions about *which* absent a good reason
> > to do so.
>
> Personally, I've worked on one platform that had no 16-bit integer
> types, and another that had no 16-bit or 32-bit integer types. On both,
> char was 8 bits and int was 64 bits. That was admittedly an unusual
> case (Cray supercomputers), and I'm not necessarily saying you shouldn't
> make that particular assumption. But it's not hard to imagine that
> future systems might violate your assumption.
It is entirely plausible that on a future hardware platform operations on
16-bit types will be much slower than operations on 32-bit or 64-bit types.
One doesn't even have to look into the future for that--such architectures
already exist. On the other hand, even if a processor has no integer types
smaller than 64 bits, there is no reason why a compiler for such a processor
couldn't be written to emulate such types. The amount of effort required to
make a compiler emulate such types such that it could then use code written
to use them would be a lot less than the amount of effort required to write
all code which should logically use such types in such a way as to work
just fine without them.
Note that if the speed at which today's code runs is adequate on today's
processors, it should remain adequate on tomorrow's processors even if they
have to use emulation logic. A modern machine running a Commodore 64
emulator could probably not run a C64 program even 1% the speed such that
an equivalent program could run if targeted for a modern machine, but it
could still run circles around an actual C64.
> > 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing
> > to standard names for 16-bit and 32-bit types were "short" and "long".
>
> long is usually 64 bits on 64-bit systems (apparently MS Windows is an
> exception). I'm not sure how common such systems were prior to 1999.
>
> If I needed exact 16-bit and 32-bit types and I was stuck with a pre-C99
> compiler, I'd probably use Doug Gwyn's q8 or something similar.
I would generally add typedefs or use those of whatever project I was
working with in cases where I cared about integer sizes, but saying:
typedef short i16;
typedef unsigned short u16;
typedef long i32;
typedef unsigned long u32;
was generally preferable to trying to write some massive bunch of #if
directives to to figure out what types should be used. In the event
that code needed to be migrated to a platform where "unsigned long"
was 64 bits but "__fnord" was a 32-bit unsigned integer, changing the
above to "typedef __fnord u32;" would be easier than trying to figure
out what needed to be patched within a bunch of more complicated
type-assignment logic.
> > 3. Writing to an object of a particular type is equivalent to overwriting
> > the underlying "char" values with that type's "char[]" representation,
> > except that types which contain padding may arbitrarily decide to copy
> > the padding or leave it alone.
>
> The standard defines a an "object representation" as a set of
> unsigned char values; it can be obtained by memcpy(), for example.
> I find that using unsigned char for raw byte values is conceptually
> cleaner. I'm vanishingly unlike to have to use a system with signed
> char using non-2's-complement with two reprentations for zero,
> but by using unsigned char I don't have to think about it.
Whether "char" or "unsigned char", my point is that in conjunction with #4,
if writing a value of some type is equivalent to translating it to a bunch
of char or unsigned char values and writing those, and reading a value as
some type is equivalent to reading a bunch of char or unsigned char values
and interpreting those as the type in question, those two statements together
imply that code which writes as one type and reads as another will be
equivalent to code which translates the first type to a char or unsigned
char representation and then interprets that char representation as the new type.
> > 5. Comparisons between unrelated pointers abide by the rule that if char*p
> > is a pointer to an object of size s, and char *q is any pointer,
> > the expression (q>=p && q<p+s) will yield one whenever q identifies part
> > of p, it will likely (though not necessarily 100% reliably) yield zero
> > when q does not identify part of p, and will never do anything other
> > than yield zero or one in any case.
>
> That's probably a valid assumption for any system with a monolithic
> memory addressing scheme. But I can imagine other schemes becoming more
> popular in the unspecified future. A scheme where each allocated object
> has its own disjoint address space could have some advantages.
Systems with a monolithic memory scheme, and even many systems without one,
could uphold a stronger guarantee which would be that that expression would
never yield true when q identified anything that wasn't in p, but I very
deliberately did not specify that. There are many kinds of applications
where a function may be given one or more source operands and a destination
operand, and be specified to write to the destination a sequence of values
that are determined be the values contained in the source operands when it
it starts, even if one or more of those operands gets overwritten during
operation. One may write such functions to always create a temporary
buffer, compute the result there, and then copy it to the destination
pointer when done, but when none of the buffers overlap it may be much
more efficient to compute the destination in place. If one can quickly
identify 100% of the cases where buffers do overlap, and 90% of those
where they don't (falsely indicating overlap 10% of the time) one may be
able to eliminate 90% of redundant copy operations. If cases which don't
require copying greatly outnumber those that do, that may be a big
performance win.
> > 7. The expression, x<<y, when y>=INTSIZE, will yield either
> > x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion;
> > right-shifts will behave likewise.
> >
> > 8. Left shifts of negative values will behave as multiplying by a power of
> > two, with the same overflow semantics described in 6.
> >
> > 9. Right shifts of negative values will be performed using sign extension.
>
> How difficult is it to avoid shifts whose behavior is not defined by the
> standard? How useful is it be able to compute, for example, n<<32,
> where n is of type uint32_t?
I would consider the expression (x<<n)|(x>>(32-n)) to be more readable than
any strictly-conforming form. In cases where one wishes to scale up a signed
value by a power of two, I would consider "value << scale" more readable
than "(int)((unsigned)value << scale)". In cases requiring Euclidian
division of a value by a power of two, right-shift is apt to be both easier
to read and faster than any strictly-conforming form.
> > My point is that behaviors like those listed above should not be considered
> > "implementation-dependent". C is the only language I know of in which the
> > set of "safe" behaviors has decreased rather than increased over the last
> > decade.
>
> Not, as far as I can tell, due to any changes in the language. (We've
> discussed that at some length before.)
I use the term "the language" to refer to the subset of features that are
consistently supported and widely used in the microcomputer marketplace. I
am quite aware that such usage goes well beyond the C89 Standard. In 1995,
anyone who suggested that a compiler targeting a platform whose left-shift
instruction works in either of the two common ways should in its normal
mode do something other than yield one of those two behaviors would have
been laughed out of town. I see no reason that should be any less true
today than it was then, though for some reason it seems to be.
> I personally would oppose making binary constants an optional
> feature. It would IMHO be worse than not adding them to the
> standard at all. The cost of implementing them is so small that
> I see no point in permitting compilers *not* to implement them.
> The value of __STDC_VERSION__ would reliably tell you whether
> they're supported (assuming a non-buggy compiler).
I'd much rather have them as a mandatory feature than not have them at
all. My point is that if the argument against adding them is that it
would impose too much work on the authors of compilers for platforms
where people would never bother to use them, such an argument wouldn't
be valid as a reason not to add them in a way which wouldn't require
compiler authors to do extra work. Having a macro indicate support
would make it possible to write code which could define BIN(x) to use
either a big nasty expression for compilers which don't support binary
literals, or else 0b##x for those which do. If a line containing such
a literal has a syntax error, the message is apt to be much more readable
in the latter case.
Besides, I suspect that the Standard started recognizing more optional
features, compiler vendors in markets where those features would be useful
would pretty quickly start feeling consumer pressure to implement them,
especially if their competitors did so.
> C99 added hexadecimal floating-point constants and made them
> mandatory for all conforming compilers.
Given a choice between optional and mandatory, I'd pick mandatory. But
if it can't be mandatory, I'd rather have a Standard acknowledge the
feature as a desirable option than pretend it doesn't exist.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-10-07 12:35 +0200 |
| Message-ID | <mv2sef$d3v$1@dont-email.me> |
| In reply to | #72088 |
On 06/10/15 23:29, Keith Thompson wrote: > supercat@casperkitty.com writes: > [...] > >> 7. The expression, x<<y, when y>=INTSIZE, will yield either >> x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion; >> right-shifts will behave likewise. >> >> 8. Left shifts of negative values will behave as multiplying by a power of >> two, with the same overflow semantics described in 6. >> >> 9. Right shifts of negative values will be performed using sign extension. > > How difficult is it to avoid shifts whose behavior is not defined by the > standard? How useful is it be able to compute, for example, n<<32, > where n is of type uint32_t? I have been involved in a discussion in comp.arch.embedded covering this case. (It happens to be in C++ rather than C, but the principle is the same.) Roughly speaking, an n-bit wide mask is created with a template (a macro would work too) using something like (1u << n) - 1. My solution was n ? ((1u << (n-1)) << 1) - 1 : 0 , because that would also work with n equal to the width of unsigned int. But I find it easy to only ever use unsigned types with shifts, then there is no problem with shifting negative numbers.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-10-07 10:57 +0200 |
| Message-ID | <mv2mlp$kvd$1@dont-email.me> |
| In reply to | #72072 |
On 06/10/15 20:18, supercat@casperkitty.com wrote: > On Tuesday, October 6, 2015 at 2:23:41 AM UTC-5, David Brown wrote: >> On 05/10/15 19:06, supercat wrote: >>> Brilliantly said. I would further suggest that there are other aspects of >>> the language where the principle applies even more strongly. Some people >>> seem to have an attitude that C programmers should in most cases try to >>> write code that would be guaranteed to work on any standards-conforming >>> compiler for any architecture, no matter how obscure. I would posit that >>> such an attitude is unrealistic and harmful. >> >> While I obviously agree with the first part of that paragraph, I am not >> so sure about the rest. There are many types of C programmers, and they >> work in many different ways for different end-uses. There are people >> who write code that will be used on a wide variety of systems - often >> they don't know what those will be while they are writing their code. >> Sticking to the standards and avoiding any extensions or >> implementation-defined behaviour (and perhaps even avoiding any standard >> behaviour that they know is not implemented in some common compilers - >> such as C11), is vital to such work. And there are others who know >> /exactly/ what tools will be in use - when I do an update on a 10-year >> old project, I do so using precisely the same tools as I did 10 years >> ago. Most programmers will lie somewhere between those extremes. > > My philosophy over the years has been that it is reasonable for most kinds > of code to assume: > > 1. It will never be called upon to run on any platform which does not have > non-padded two's-complement and unsigned 16-bit and 32-bit integer types > available which are stored either big-endian or little-endian format, > but code should not make assumptions about *which* absent a good reason > to do so. Agreed. > > 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing > to standard names for 16-bit and 32-bit types were "short" and "long". > I sort of agree - but if I use a pre-C99 compiler that doesn't have <stdint.h> (and I do have old projects with such compilers), then I make my own "stdint.h" with appropriate typedefs. The compiler-specific parts are therefore kept to a minimum. > 3. Writing to an object of a particular type is equivalent to overwriting > the underlying "char" values with that type's "char[]" representation, > except that types which contain padding may arbitrarily decide to copy > the padding or leave it alone. > Agreed. > 4. Reading an object of a particular type is equivalent to reading the > underlying "char" values and interpreting them as the type in question. > Agreed. > 5. Comparisons between unrelated pointers abide by the rule that if char*p > is a pointer to an object of size s, and char *q is any pointer, > the expression (q>=p && q<p+s) will yield one whenever q identifies part > of p, it will likely (though not necessarily 100% reliably) yield zero > when q does not identify part of p, and will never do anything other > than yield zero or one in any case. Mostly agreed. I have some targets where pointers to data in flash are unrelated to pointers to data in ram (since access to flash and ram use different cpu instructions), which makes some pointer handling more complex. The simple workaround is, of course, never to make comparisons between these different types of pointers - then the issue doesn't arise. > > 6. In the absence of absented documentation to the contrary, arithmetic > overflow on integer types will yield a Partially-Indeterminate Value > with the semantics that every rvalue conversion will yield a number > which is congruent to the arithmetically correct value modulo the > range of the type, but might not necessarily be within the range of > the type. If such a value is stored in a variable, repeated rvalue > conversions may yield different values subject to that constraint. Disagreed. Compilers may make use of the undefined behaviour here for optimisation purposes. I think it is safe (for the same sense of "safe" as the rest of your rules) to assume that in a calculation, integer overflow will be done as simple wrap-around for two's complement arithmetic, since that will be the shortest and fastest implementation in almost all circumstances. But any clear reliance on this behaviour, such as using "if (x < 0)" to attempt to check for overflow, is wrong. You can, however, rely on the compiler not inserting additional checks such as "if (overflow) launchNasalDaemons();". > > 7. The expression, x<<y, when y>=INTSIZE, will yield either > x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion; > right-shifts will behave likewise. Disagreed (as above). > > 8. Left shifts of negative values will behave as multiplying by a power of > two, with the same overflow semantics described in 6. > Disagreed (as above). Do your multiplies as multiplies, and let the compiler turn them into shifts. > 9. Right shifts of negative values will be performed using sign extension. > Disagreed (as above). Also, some platforms have only logical right shift instructions - implementing sign-extended behaviour would be costly. > I know of very few cases where where straightforwardly-generated code which > targets a modern machine, would work in cases mandated by the C Standard, > and would behave consistent with the above behaviors 1-9, would be any more > expensive than the optimal straightforwardly-generated code which was not > required to abide by 1-9, and I until recently the default behavior of > every compiler I've used (and I've used quite a few, for a wide range of > platforms) would be to uphold the above guarantees even in those rare cases > where it might require an extra instruction or two (e.g. on processors > without an arithmetic-right-shift instruction). > > IMHO, there is no good reason why the above behaviors should not be > recognized as common extensions, which should be supported in the absence > of a compelling reason to do otherwise. To be of any use (i.e., to be something that developers can rely on), these would have to be a fixed set of rules - a "normal system" extension. It would be important that any C system supporting this, support all aspects of it - if you allow some parts to vary according to "compelling reason", you might as well stick to the current implementation defined behaviour. The point here would be to define a set of "normal" implementation-defined behaviour rules, along with making some undefined behaviour be defined. I would be happy to see something along these lines become an official extension - not part of the main C standard, but overseen by an ISO group. (An extension for embedded systems, including 0b binary literals, has been mentioned. The two extensions here would be orthogonal.) > Being *explicitly* told by a > programmer "this section of code does not require certain commonly-provided > guarantees" would be a compelling reason. The fact that a particular > hardware platform would make code which upheld a certain guarantee 50% > slower than it would have to be otherwise could be a compelling reason. > The theoretical possibility that it might be possible to make some code > meet its behavioral obligations more quickly would not be such a compelling > reason. > >> If the compiler I am using for a project provides useful extensions, I >> will use them when they make a difference. Some of my code is reused >> between projects with different tools - for such code, unusual >> extensions should be avoided (or hidden behind macros). But much of it >> is either project specific, or will be re-used in projects with similar >> tools - extensions then make sense. As a small systems (bare bones) >> embedded programmer, my programs /always/ need at least some sort of >> extensions or tool-specific code for hardware interaction, embedded >> assembly, interrupt handling, etc. > > Have you used any platforms which would have any difficulty meeting any of > the above guarantees? Yes, for some of your list - as noted above. That applies both to compilers that don't follow your list, and platforms that make it hard or inefficient to do so. > Some platforms would be able to offer useful > guarantees beyond the above (e.g. some will allow code to test for object > overlap without any false positives, though e.g. some 8086 compilers will > not). Code which relies upon the guarantees above can in many cases be > clearer than any comparably-efficient code which does not (if comparable > efficiency without those guarantees is even possible). > While I am not convinced by all of your list, I certainly agree that sometimes target-specific code that makes use of known implementation-defined behaviour can be clearer, neater, and more efficient than code that avoids any implementation-defined behaviour. I make use of such code, balancing the potential benefits of portability, the likelihood of differences in implementation-defined behaviour, and the benefits to the code at hand. As a simple example, I make heavy use of uint8_t, even though it does not exist on all platforms. > While it may be useful to be able to use hardware that cannot efficiently > offer those guarantees to run programs that don't require them, I see no > reason to believe that future hardware will be any less capable of honoring > them than present hardware. While I recognize that the value of assumptions > about what things will or won't alias, the rules about type-punning are > simultaneously insufficient to identify most cases where a compiler could > safely and usefully assume things won't alias, but so restrictive as to > preclude many useful programming techniques. > >> But I make a point of avoiding relying on implementation dependant >> behaviour for "normal" C code. Even if I know the code I am writing >> will only ever be run on this particular 16-bit microcontroller, I'll >> use int16_t rather than int when I want a 16-bit integer. I always know >> if my target is little-endian or big-endian - but any code that depends >> on that difference will be minimised, and well commented. > > My point is that behaviors like those listed above should not be considered > "implementation-dependent". C is the only language I know of in which the > set of "safe" behaviors has decreased rather than increased over the last > decade. > >> Binary literals are already implemented on most compilers targeting >> embedded systems. In most cases, they use the 0b format - but there are >> some exceptions for toolchains that had implemented binary literals >> before 0b became the de facto standard. However, these are mostly for >> smaller and more painful microcontrollers, and code written for such >> devices is typically very highly specialised to the microcontroller and >> compiler. > > Do you know of any situations in which different compilers that accept 0bXXX > literals would not interpret them the same way, or can you think of any > reason why a compiler would want to? No (baring possible bugs in implementations). > If not, what disadvantages would you > see to having a Standard indicate that implementations which define a > certain macro (e.g. __STDC_BINARY_LITERALS) with a non-zero value are > required to interpret literals of that form the way that existing > implementations do? The only costs imposed by such a rule would be: > > 1. Compilers which implement such literals should add a predefined macro > indicating that. > > 2. Compilers which don't implement such literals must refrain from using the > identifier __STDC_BINARY_LITERALS for some other purpose. > > Cost #1 seems pretty small, and cost #2 negligible. > /I/ see no problem with just adding 0b binary literals to the standard for the next version of C. But I understand that other people /do/ see it as a problem.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-07 07:09 -0700 |
| Message-ID | <8d9e6817-ba7d-4802-9636-7862361c3f11@googlegroups.com> |
| In reply to | #72120 |
On Wednesday, October 7, 2015 at 3:57:38 AM UTC-5, David Brown wrote:
> On 06/10/15 20:18, supercat wrote:
> > 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing
> > to standard names for 16-bit and 32-bit types were "short" and "long".
> >
>
> I sort of agree - but if I use a pre-C99 compiler that doesn't have
> <stdint.h> (and I do have old projects with such compilers), then I make
> my own "stdint.h" with appropriate typedefs. The compiler-specific
> parts are therefore kept to a minimum.
I agree that if code needs some types to be certain sizes, it should define
typedefs for them, but I think it is clearer and more helpful to have a
simple bunch of definitions at the start of a file which is included by
everything else:
typedef unsigned char u8; // Not available on some DSPs
typedef signed char s8; // Not available on some DSPs
typedef unsigned short u16;
typedef signted short s16;
typedef unsigned long u32;
typedef signted long s32;
and figure that those will be changed as needed for nay new target platform,
though the above definitions have been usable on most compilers for over
20 years, and could be readily adapted to map those app-defined types to
uint32_t, etc.
> > 5. Comparisons between unrelated pointers abide by the rule that if char*p
> > is a pointer to an object of size s, and char *q is any pointer,
> > the expression (q>=p && q<p+s) will yield one whenever q identifies part
> > of p, it will likely (though not necessarily 100% reliably) yield zero
> > when q does not identify part of p, and will never do anything other
> > than yield zero or one in any case.
>
> Mostly agreed. I have some targets where pointers to data in flash are
> unrelated to pointers to data in ram (since access to flash and ram use
> different cpu instructions), which makes some pointer handling more
> complex. The simple workaround is, of course, never to make comparisons
> between these different types of pointers - then the issue doesn't arise.
Even on those cases, will a comparison between unrelated pointers do
anything other than yield 0 or 1? Not all systems provide an efficient
means of testing pointer overlap that avoids false positives, but there
are a fair number of algorithms which can be implemented as:
if pointers might overlap
do operation slow way
else
do operation fast way that will malfunction if pointers overlap
How many systems are there where the above approach would not be usable
to improve efficiency if pointer overlaps are rare?
> > 6. In the absence of absented documentation to the contrary, arithmetic
> > overflow on integer types will yield a Partially-Indeterminate Value
> > with the semantics that every rvalue conversion will yield a number
> > which is congruent to the arithmetically correct value modulo the
> > range of the type, but might not necessarily be within the range of
> > the type. If such a value is stored in a variable, repeated rvalue
> > conversions may yield different values subject to that constraint.
>
> Disagreed. Compilers may make use of the undefined behaviour here for
> optimisation purposes. I think it is safe (for the same sense of "safe"
> as the rest of your rules) to assume that in a calculation, integer
> overflow will be done as simple wrap-around for two's complement
> arithmetic, since that will be the shortest and fastest implementation
> in almost all circumstances. But any clear reliance on this behaviour,
> such as using "if (x < 0)" to attempt to check for overflow, is wrong.
> You can, however, rely on the compiler not inserting additional checks
> such as "if (overflow) launchNasalDaemons();".
I do not regard it safe to make any assumptions about whether integer
overflows wrap unless a compiler explicitly promises that, and I do not
consider it good style to do so even given such documentation. Perhaps
I should be clearer about what I expect:
int i=32767;
long j=++i,k=i,q=4,l=i;
Given the above, I would not consider it remotely surprising for j,k,l to
be assigned (+32768,+32768,-32768), (+32768,-32768,-326768), or
(-32768,-32768,-32768); indeed, I think I've used systems which would have
each of those results. I've also seem systems which have optimization
options which are *documented* as changing the semantics of loops in which
overflows may occur [thus satisfying my "in the absence of documentation
to the contrary" requirement].
> > 7. The expression, x<<y, when y>=INTSIZE, will yield either
> > x<<(y & INTSIZE) or x<<(y-1)<<1, chosen in unspecified fashion;
> > right-shifts will behave likewise.
>
> Disagreed (as above).
There are some systems where 1<<-1 will lock up the bus for about -1U
bus cycles while they iterate the left-shift operation, but I would regard
that implementation as using the recurrence x<<y = x<<(y-1)<<1. The only
implementations I can even imagine that would do something other than one
of the above would be those which use a jump table to select a shifting
routine, and given that the cost of adding code to prevent the table from
going off the rails would be small compared with even the *best case*
execution time, the Principle-Of-Least-Astonishment compliant behavior for
such a compiler would be to include such code.
> > 8. Left shifts of negative values will behave as multiplying by a power of
> > two, with the same overflow semantics described in 6.
> >
>
> Disagreed (as above). Do your multiplies as multiplies, and let the
> compiler turn them into shifts.
For scaling by a variable power of two, I would consider x<<y to be no less
clear than x*(1<<y). In many cases where speed is any sort of consideration,
use of the latter would require inspecting generated code to ensure it does
not perform a needless slow multiplication step--a process which would not
otherwise be necessary. While there would usually not be any performance
penalty with converting to unsigned, doing the shift, and converting the
result to signed, I think that's less clear than simply doing the left shift
as signed.
> > 9. Right shifts of negative values will be performed using sign extension.
> >
>
> Disagreed (as above). Also, some platforms have only logical right
> shift instructions - implementing sign-extended behaviour would be costly.
I've used platforms which lack logical shift instructions, and every single
one of them has added the code necessary for arithmetic left shift for
signed values, even when logical right shift would have been faster. One
vendor, when asked about that on a discussion board, observed that code
which wants a logical right shift can cast to unsigned, but having signed
values not use arithmetic right shift would make it harder to write code
which requires that operation.
> To be of any use (i.e., to be something that developers can rely on),
> these would have to be a fixed set of rules - a "normal system"
> extension. It would be important that any C system supporting this,
> support all aspects of it - if you allow some parts to vary according to
> "compelling reason", you might as well stick to the current
> implementation defined behaviour. The point here would be to define a
> set of "normal" implementation-defined behaviour rules, along with
> making some undefined behaviour be defined.
IMHO, there should be a way by which programs can specify that they need
certain behaviors beyond the minimums established by the Standard, and
compilers would be required to either provide the behaviors or refuse
compilation. There should also be a standard for conformance which would
describe programs which will work correctly **on all compilers that are
allowed to accept them**.
As it is, the Standard only defines two kinds of conforming programs--those
which are "strictly conforming", meaning that all legitimate implementations
must make them yield correct behavior, and "conforming", meaning that there
exists at least one C implementation somewhere which will cause it to yield
correct behavior. Very few programs could reasonably be written to be
strictly conforming, and knowing that a program is "conforming" says nothing
useful about what it will or won't do on any particular system.
Adding a level of conformance which would allow a program to make use of
optional features provided that indicates its need for such features in a
standardized way would make it possible for the majority of useful C
programs to have a meaningfully-defined level of standard conformance,
thus greatly enhancing the utility of the standard.
> I would be happy to see something along these lines become an official
> extension - not part of the main C standard, but overseen by an ISO
> group. (An extension for embedded systems, including 0b binary
> literals, has been mentioned. The two extensions here would be orthogonal.)
I don't think the above behaviors should be required in all C compilers,
since there are some (by now mostly obscure) platforms where some of them
could be problematic. I would suggest, however, that the increasingly-
popular notion that compilers shouldn't endeavor to make such behaviors
useful needs to be removed from polite society.
> Yes, for some of your list - as noted above. That applies both to
> compilers that don't follow your list, and platforms that make it hard
> or inefficient to do so.
How carefully have you read the points with which you stated disagreement?
Some you didn't like, but didn't mention having seen any implementations
which behaved contrary to what I actually specified.
> While I am not convinced by all of your list, I certainly agree that
> sometimes target-specific code that makes use of known
> implementation-defined behaviour can be clearer, neater, and more
> efficient than code that avoids any implementation-defined behaviour. I
> make use of such code, balancing the potential benefits of portability,
> the likelihood of differences in implementation-defined behaviour, and
> the benefits to the code at hand. As a simple example, I make heavy use
> of uint8_t, even though it does not exist on all platforms.
My point is that programmers should be able to use behaviors such as those
on my list without sacrificing the ability to port their programs to any
non-obscure platforms. I suspect there are a lot more platforms that use
16-bit "char" than there are platforms where the above assumptions would
be problematic (noting that even compiler vendors for processors without
arithmetic-shift instructions *still* tend to provide arithmetic right
shift in software).
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2015-10-08 09:28 +0200 |
| Message-ID | <mv55r6$9pd$1@dont-email.me> |
| In reply to | #72137 |
On 07/10/15 16:09, supercat@casperkitty.com wrote: > On Wednesday, October 7, 2015 at 3:57:38 AM UTC-5, David Brown wrote: >> On 06/10/15 20:18, supercat wrote: >>> 2. Prior to C99's definitions for int16_t, int32_t, etc. the closest thing >>> to standard names for 16-bit and 32-bit types were "short" and "long". >>> >> >> I sort of agree - but if I use a pre-C99 compiler that doesn't have >> <stdint.h> (and I do have old projects with such compilers), then I make >> my own "stdint.h" with appropriate typedefs. The compiler-specific >> parts are therefore kept to a minimum. > > I agree that if code needs some types to be certain sizes, it should define > typedefs for them, but I think it is clearer and more helpful to have a > simple bunch of definitions at the start of a file which is included by > everything else: > > typedef unsigned char u8; // Not available on some DSPs > typedef signed char s8; // Not available on some DSPs > typedef unsigned short u16; > typedef signted short s16; > typedef unsigned long u32; > typedef signted long s32; > > and figure that those will be changed as needed for nay new target platform, > though the above definitions have been usable on most compilers for over > 20 years, and could be readily adapted to map those app-defined types to > uint32_t, etc. I used to use my own sized types, before <stdint.h>, but I can't see any reason to continue doing so - even when working with C90 code. In practice they are usually defined in a common header (as you suggest) rather than my own <stdint.h>, but that's just a minor detail. > >>> 5. Comparisons between unrelated pointers abide by the rule that if char*p >>> is a pointer to an object of size s, and char *q is any pointer, >>> the expression (q>=p && q<p+s) will yield one whenever q identifies part >>> of p, it will likely (though not necessarily 100% reliably) yield zero >>> when q does not identify part of p, and will never do anything other >>> than yield zero or one in any case. >> >> Mostly agreed. I have some targets where pointers to data in flash are >> unrelated to pointers to data in ram (since access to flash and ram use >> different cpu instructions), which makes some pointer handling more >> complex. The simple workaround is, of course, never to make comparisons >> between these different types of pointers - then the issue doesn't arise. > > Even on those cases, will a comparison between unrelated pointers do > anything other than yield 0 or 1? On the system that I know about (gcc on the AVR microcontroller), yes, pointers to these different address spaces can be compared without nasal daemons, and the result will be 0 or 1. It won't be meaningful in any way, so there is no reason to have such code in a program, but it will not cause problems. > Not all systems provide an efficient > means of testing pointer overlap that avoids false positives, but there > are a fair number of algorithms which can be implemented as: > > if pointers might overlap > do operation slow way > else > do operation fast way that will malfunction if pointers overlap > > How many systems are there where the above approach would not be usable > to improve efficiency if pointer overlaps are rare? In systems where you have pointers to different types of address spaces, the issues here simply do not occur. The pointers (or objects they point to) can't overlap if they are in different address spaces, even if the underlying integral values of the pointers appear to overlap. And the copying (or whatever operation you are doing with them) cannot possibly work if you don't consider the type of address space involved. Thus while a traditional "memmove" will use such overlap logic for efficient (and correct!) behaviour, on a chip like the AVR you will have memcpy_P which is a variant of memcpy but the source data is in flash. There is no memmove_P in the avrlibc library, because it doesn't make sense to worry about overlap. > >>> 8. Left shifts of negative values will behave as multiplying by a power of >>> two, with the same overflow semantics described in 6. >>> >> >> Disagreed (as above). Do your multiplies as multiplies, and let the >> compiler turn them into shifts. > > For scaling by a variable power of two, I would consider x<<y to be no less > clear than x*(1<<y). In many cases where speed is any sort of consideration, > use of the latter would require inspecting generated code to ensure it does > not perform a needless slow multiplication step--a process which would not > otherwise be necessary. While there would usually not be any performance > penalty with converting to unsigned, doing the shift, and converting the > result to signed, I think that's less clear than simply doing the left shift > as signed. I disagree here. I don't see any need of doing shifts of any sort on signed types. I think "x * (1u << y)" is nicer than "x << y" for multiplying a signed integer x by a power of 2, because to my mind it makes the intention clearer (and has no issues if x is negative). But this is all a matter of opinion. > >>> 9. Right shifts of negative values will be performed using sign extension. >>> >> >> Disagreed (as above). Also, some platforms have only logical right >> shift instructions - implementing sign-extended behaviour would be costly. > > I've used platforms which lack logical shift instructions, and every single > one of them has added the code necessary for arithmetic left shift for > signed values, even when logical right shift would have been faster. One > vendor, when asked about that on a discussion board, observed that code > which wants a logical right shift can cast to unsigned, but having signed > values not use arithmetic right shift would make it harder to write code > which requires that operation. If the cpu cannot handle logical right shifts efficiently, then the compiler will just have to generate inefficient code for that purpose. The compiler is, of course, free to generate arithmetic shift opcodes when you write "x / 2". > >> To be of any use (i.e., to be something that developers can rely on), >> these would have to be a fixed set of rules - a "normal system" >> extension. It would be important that any C system supporting this, >> support all aspects of it - if you allow some parts to vary according to >> "compelling reason", you might as well stick to the current >> implementation defined behaviour. The point here would be to define a >> set of "normal" implementation-defined behaviour rules, along with >> making some undefined behaviour be defined. > > IMHO, there should be a way by which programs can specify that they need > certain behaviors beyond the minimums established by the Standard, and > compilers would be required to either provide the behaviors or refuse > compilation. There should also be a standard for conformance which would > describe programs which will work correctly **on all compilers that are > allowed to accept them**. > > As it is, the Standard only defines two kinds of conforming programs--those > which are "strictly conforming", meaning that all legitimate implementations > must make them yield correct behavior, and "conforming", meaning that there > exists at least one C implementation somewhere which will cause it to yield > correct behavior. Very few programs could reasonably be written to be > strictly conforming, and knowing that a program is "conforming" says nothing > useful about what it will or won't do on any particular system. > > Adding a level of conformance which would allow a program to make use of > optional features provided that indicates its need for such features in a > standardized way would make it possible for the majority of useful C > programs to have a meaningfully-defined level of standard conformance, > thus greatly enhancing the utility of the standard. > >> I would be happy to see something along these lines become an official >> extension - not part of the main C standard, but overseen by an ISO >> group. (An extension for embedded systems, including 0b binary >> literals, has been mentioned. The two extensions here would be orthogonal.) > > I don't think the above behaviors should be required in all C compilers, > since there are some (by now mostly obscure) platforms where some of them > could be problematic. I would suggest, however, that the increasingly- > popular notion that compilers shouldn't endeavor to make such behaviors > useful needs to be removed from polite society. > >> Yes, for some of your list - as noted above. That applies both to >> compilers that don't follow your list, and platforms that make it hard >> or inefficient to do so. > > How carefully have you read the points with which you stated disagreement? > Some you didn't like, but didn't mention having seen any implementations > which behaved contrary to what I actually specified. One direct example for one of the points is the AVR, which has separate flash and data address spaces. I also feel that some of the cases where you want to impose definitions (at least implementation-defined or unspecified behaviour, rather than undefined behaviour) could limit some optimisation possibilities. I am not sure that's a big issue, however, and there could be many advantages in turning some cases of undefined behaviour into unspecified behaviour which would make some code a lot easier to write safely. For example, signed integer overflow could be made unspecified (it's always an integer, you just don't know which one) rather than undefined (it could trap, it could launch nasal daemons). > >> While I am not convinced by all of your list, I certainly agree that >> sometimes target-specific code that makes use of known >> implementation-defined behaviour can be clearer, neater, and more >> efficient than code that avoids any implementation-defined behaviour. I >> make use of such code, balancing the potential benefits of portability, >> the likelihood of differences in implementation-defined behaviour, and >> the benefits to the code at hand. As a simple example, I make heavy use >> of uint8_t, even though it does not exist on all platforms. > > My point is that programmers should be able to use behaviors such as those > on my list without sacrificing the ability to port their programs to any > non-obscure platforms. I suspect there are a lot more platforms that use > 16-bit "char" than there are platforms where the above assumptions would > be problematic (noting that even compiler vendors for processors without > arithmetic-shift instructions *still* tend to provide arithmetic right > shift in software). >
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-08 01:45 -0700 |
| Message-ID | <3f1c5dce-1bf7-4536-8764-1443056d72a5@googlegroups.com> |
| In reply to | #72182 |
On Thursday, October 8, 2015 at 2:28:41 AM UTC-5, David Brown wrote: > On 07/10/15 16:09, supercat wrote: > > Even on those cases, will a comparison between unrelated pointers do > > anything other than yield 0 or 1? > > In systems where you have pointers to different types of address spaces, > the issues here simply do not occur. The pointers (or objects they > point to) can't overlap if they are in different address spaces, even if > the underlying integral values of the pointers appear to overlap. And > the copying (or whatever operation you are doing with them) cannot > possibly work if you don't consider the type of address space involved. On some systems like the 8x51 (I've not used AVR) compilers often have multiple kinds of pointers available--one for each address space, plus a "universal" pointer type which uses a byte to select which address space it should use. I'm not sure which style of pointer you're referring to. In any case, I've worked with 8086 compilers where pointers in different segments could be falsely reported as overlapping, but where comparisons were still useful because they allowed performance improvements in the cases where they correctly identified non-overlap scenarios, and the only consequence when they were wrong was that code would sometimes miss speed-up opportunities. > >>> 8. Left shifts of negative values will behave as multiplying by a power of > >>> two, with the same overflow semantics described in 6. > I disagree here. I don't see any need of doing shifts of any sort on > signed types. I think "x * (1u << y)" is nicer than "x << y" for > multiplying a signed integer x by a power of 2, because to my mind it > makes the intention clearer (and has no issues if x is negative). But > this is all a matter of opinion. I've used compilers where there would be a huge performance difference between x<<y and x*(1<<y). While many compilers are likely to optimize the latter expression, it's a sufficiently complicated expression that I wouldn't bet money on a compiler doing so unless I'd hand-inspected the code it generates. > >>> 9. Right shifts of negative values will be performed using sign extension. > If the cpu cannot handle logical right shifts efficiently, then the > compiler will just have to generate inefficient code for that purpose. > The compiler is, of course, free to generate arithmetic shift opcodes > when you write "x / 2". I meant that some CPUs lack arithmetic right-shift opcodes (though in many cases they lack logical shift as well and only support rotate-through- carry). Although compilers will often manage to avoid division instructions when dividing by 2, if x is signed then x/2 is not equivalent to x>>1 but rather to (x-(x>>31))>>1. > One direct example for one of the points is the AVR, which has separate > flash and data address spaces. I also feel that some of the cases where > you want to impose definitions (at least implementation-defined or > unspecified behaviour, rather than undefined behaviour) could limit some > optimisation possibilities. I am not sure that's a big issue, however, > and there could be many advantages in turning some cases of undefined > behaviour into unspecified behaviour which would make some code a lot > easier to write safely. For example, signed integer overflow could be > made unspecified (it's always an integer, you just don't know which one) > rather than undefined (it could trap, it could launch nasal daemons). My whole purpose is to prevent "optimizations" which replace a program that would have met requirements with a "more efficient" program which does not. A compiler should be a tool whose first and foremost objective is to help programmers generate executables that meet requirements. The philosophy of using UB for optimization is fundamentally backward for any task which requires that programs accept input from untrusted sources. The only programs where UB-based optimization will achieve any useful effect which could not be achieved other ways would be those which will invoke UB in response to some inputs; since the optimizer could very easily turn what would have been "benign" forms of UB into security vulnerabilities, the only time such optimizations improve "efficiency" is when producing code that does not meet requirements.
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-10-08 09:42 +0000 |
| Message-ID | <mv5dq6$2jc$1@speranza.aioe.org> |
| In reply to | #72185 |
supercat@casperkitty.com wrote: (snip) > On some systems like the 8x51 (I've not used AVR) compilers often have > multiple kinds of pointers available--one for each address space, plus > a "universal" pointer type which uses a byte to select which address > space it should use. I'm not sure which style of pointer you're referring > to. > In any case, I've worked with 8086 compilers where pointers in different > segments could be falsely reported as overlapping, but where comparisons > were still useful because they allowed performance improvements in the > cases where they correctly identified non-overlap scenarios, and the > only consequence when they were wrong was that code would sometimes miss > speed-up opportunities. With multiple address spaces, or a segmented address space, pointer equality and inequality should test both address space or segment and offset. Comparisons <, <=, >, >=, only need to compare the offset. Depending on how you do the overlap comparison, you might get a surprising answer. -- glen
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-08 09:42 -0700 |
| Message-ID | <44d29ab5-a2fe-49f9-b6b7-5d986b337afb@googlegroups.com> |
| In reply to | #72187 |
On Thursday, October 8, 2015 at 4:42:46 AM UTC-5, glen herrmannsfeldt wrote: > With multiple address spaces, or a segmented address space, > pointer equality and inequality should test both address space > or segment and offset. Comparisons <, <=, >, >=, only need to > compare the offset. > > Depending on how you do the overlap comparison, you might get > a surprising answer. Whether you get a surprising answer also depends on your expectations. If the implementation always yields 0 or 1 with no other side-effect, and if the result of the comparison only really *matters* in the case where pointers are related, one shouldn't find the answer too surprising.
[toc] | [prev] | [next] | [standalone]
Page 1 of 3 [1] 2 3 Next page →
Back to top | Article view | comp.lang.c
csiph-web