C, really portable?

N

Netocrat

On Sun, 11 Dec 2005 00:59:45 +0100, jacob navia wrote:
[detecting errors in printf formats]
Lcc-win32 does that kind of analysis but this is not the
point, since the format string could be an argument
or a variable passed from another function, etc.

So you seem to be arguing that the programmer's responsibility to
validate input should be deferrable to the printf function.

If the format string isn't validated first though, it may cause printf to
try to parse more variable arguments than it was called with.

If the string is built up out of a number of simple cases where that is
known to be impossible, then correct code ensures that the string is valid
- a specific error return from printf for "invalid format string" may be
useful for debugging purposes though.

Btw, is the recursive function that I posted for strrepl suitable for your
purposes?
 
M

Mark McIntyre


(snip complicated and fairly poor example of error handling)
As the programmer gets better, the code gets more unreadable, less portable,
less maintainable, and more prone to bugs.

Only if the programmer has never heard of libraries.
 
N

Netocrat

On Sun, 11 Dec 2005 11:08:33 -0500, Eric Sosman wrote:
[discriminating fprintf errors]
f = fprintf(....);
if (r < 0) {
perror ("fprintf"); /* debatable */
...
}

The perror() call is dubious because the Standard doesn't
require that fprintf() set errno to anything meaningful.

It does guarantee that fprintf won't set errno to zero though (it may set
it to non-zero). So the code could be extended:

errno = 0;
f = fprintf(....);
if (r < 0) {
if (errno > 0)
perror ("fprintf"); /* debatable */
else
fputs(stderr, "fprintf: unspecified error");
...
}
 
M

Malcolm

Richard Heathfield said:
I didn't notice any particular evidence of a good programmer at work in
the
code you posted.
It was a code snippet. When you post a function you don't usually include
the headers it relies on.
 
J

Joe Wright

Malcolm said:
It was a code snippet. When you post a function you don't usually include
the headers it relies on.
Malcom, you have terminated the printf statement with ':' not ';'. Hence
no evidence of good programming.
 
S

Skarmander

Joe said:
Malcom, you have terminated the printf statement with ':' not ';'. Hence
no evidence of good programming.
Indeed. Personally, I think people capable of typos shouldn't program. Ever.
Typos like these cost companies millions every year.

Deplorable.

S.
 
M

Malcolm

P.J. Plauger said:
Wait, is Java a modern language superior to C, or is it still
going through growing pains?
I think it is both. It is a much better OO language than C++, since the
designers learnt from the mistakes.
A big advantage over C is that you have automatic memory allocation. No more
messing about with testing mallocs() and then painstaking freeing your
half-built structure on fail, just for a refusal to give you a hundred bytes
that can never happen.
A big disadvantage in relation to C is the automatic memory allocation -
now we can't fine tune the prgoram where we need performance.
A popular dialect of
C (never standardized) used far and near pointers, to good
effect, to deal with a practical architectural issue. (They
even proved useful on a couple of non-X86 platforms as well.)
Whatever problems they caused pale compared to the races,
deadlocks, and sheer lack of portability inherent in Java
threading.
All practical C programs use pointers. Most Java programs don't need
threads.

What I have never done is written an all-singing, all-dancing Java
interactive application. I just use it for providing a simple graphical UI.
I would prefer C, but there is no standard windowing library that all
platforms (with appropriate hardware) are guaranteed to support.

This would be a trivial job - just define half a dozen function to open a
window, close it, draw a pixel, query the mouse position, and synchronise
the screen. However no-one has done so, and it is non-trival to implement
such an interface given another a high-level interface.
Java indeed "solved" a number of problems that C never has,
like flexible types and arithmetic representations. C hasn't
solved them because some of understand that they're not
problems.
It depends what you are doing, Java aims for rigorous portability - the same
program produces the same output, regardless of platform. Obviously, we
would chose this, other things being equal.
C aims for the next level of portability - the same program produces correct
output, regardless of platform.
So let's say my task is to load in a model of a molecule, rotate it given
user angles, and then output the result as a JPEG image. If the patron
specifies that the JPEG file must be, byte for byte, exactly the same on
every platform, then Java is the language to go for. There might be good
reasons for demanding this - for instance, you can test the program
automatically. Normally, however, the patron is happy with an image that he
can view, and shows the molecule in the orientation he entered.
Right. And that's exactly what you do with *every* program
that purports to be portable in the real world. Try telling
a project manager, "Don't bother to test the Java (or Perl,
or whatever) components before shipping -- they're guaranteed
to work on all supported platforms." Uh huh.
But this is an unacceptable situation, which we must work to change. An
engineer working for a bolt factory expects to have to test his bolts for
stress, manufacturing tolerances, or whatever. The engineer building a
bridge expects to order the bolts, and for them to do what it says on the
box. That's the way it should be with software.
I agree that writing portable code takes effort, which is
why I often advise people not to aim for portability unless
they're really sure they'll actually do one or more ports.
Otherwise, you'll never recoup the investment. I simply
observe that when I do choose to invest in portable C code,
the extra cost is not that great for the reward in extra
usability/marketability.
I think a common mistake is to suppose that the main benefit in writing
portbale code is to be able to compile and run it on a different platform.
It sounds commonsensical. In fact, by making code portable, you improve the
logical structure of your program, making it more readable, and less likely
to contain errors. That is often a much greater benefit.
Probably written in C, and callable from C. So you think a
programming language has to ship with a huge library to call
itself portable? I don't.
I don't know about that one. I use Java for trivial GUI apps, precisely
because C has no library. I would prefer to keep all my code in the same
language.
All of those are nice, and all of them are available in most of
the C compilers I use. Requiring them to be in the standard
library shipped with a language translator is, however, a
two-edged sword.
With most of these things, you have to choose between them and pointers. It
is easy enough to serialise a data structure, as long as it has no pointers.
It is a bit harder to provide threading, but a lot harder still if you
cannot protect memory. Same for arrays and memory leaks.
C is the language of pointers.
Excuse me, but you made two statements about things that go on
inside my head. And you happen to be wrong. You're defending them
by stating opinions that come from inside *your* head. And I
happen to believe most of those opinions are not supported in
the real world. I am undisputably the world's foremost authority
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
on what goes on inside my head. You seem to have problems ^^^^^^^^^^^^^^^^^^^^^^^^
distinguishing my reality from your personal reality from the
real world we both inhabit. I try not to.
How do you know you are not suffering from a mental illness?
>

Right. But that's not the *only* thing I've said, by a
long shot. Your syllogisms are once again bass-ackwards.
I'm using Fortran 77. When I started my new position, everyone was using it,
because it was the standard for academic programming. I decided to put IO
and memory allocation into C, and keep the numerical part of the program in
Fortran - I think it was the right decision but two months into starting,
all we've really achieved is to convert existing assets from Fortran 77 to
C, so I won't now do the scientific work I had hoped to do by Christmas.
I'm working now, in December 2005. So for me Fortran 77 is very much still a
modern language.
Sigh. What I said was that you make a Boolean out of a quantity
by using it in a comparison predicate. I used that as an
example of how people can conclude that a program is "not
portable" by performing some sort of implicit or explicit
quantitative estimate. The point is that portability
becomes a Boolean *only* when you apply a predicate to it.
Sure. I'd say that a very important rule is "will this program be usable if
compiled with zero changes to the source?". Even if you need to make a
trivial change, e.g. converting MAX_PATH to _MAX_PATH, you require a
competent programmer, and the value of the source is diminished.
However even this isn't completely Boolean. What about source which
compiles, but on Unix require an "-lm" to be added to the the makefile /
compiler options? This is a similar irritation, but not as bad as the source
change. In a lot of environments, the rule will be "OK, the source has
changed, now please rerun all the test suites", but not if a compiler flag
changes.
Other people have challenged your presumption better than I can.
When I use Perl, I usally use it for tasks that are not portable, because it
ships as source and hacking into a perl script is less intimidating than
recompiling C source. So if you want to load a thousand models from
/freddy/models, all starting with "att" and with digits between 0-52 as
suffixes, Perl is ideal. The task is, of course, very much dependent on the
needs of the moment.
 
M

Malcolm

Eric Sosman said:
The only way to keep the list to a manageable length is
to group multiple error conditions together -- and as soon
as you do so, of course, you're suppressing information
about the "root cause" error. You keep mentioning a "disk
full" error -- but is that really the root cause? No, the
disk actually has plenty of room but your account has reached
its quota. Even that isn't the ultimate cause, because the
I/O system automatically contacted the billing system to see
if it could purchase some more quota for you on the fly, and
the ultimate I/O error is "Credit card rejected."

int r = printf("hello, world\n");
if (r < 0) {
switch (r) {
case IOERR_DISK_FULL: ...; break;
case IOERR_OVER_QUOTA: ...; break;
case IOERR_NETWORK_FAILED: ...; break;
case IOERR_DISK_FAILED: ...; break;
/* etc. */
}

What, exactly, would replace each set of ellipses, and how
would your handling of IOERR_BAD_MEMORY_PARITY differ from
IOERR_BLACKBERRY_LOST_PATENT_FIGHT_AND_TERMINATED_SERVICE?
So what the user wants is

"This program could not complete because the disk is full. You have arranged
to purchase disk space on demand, but your automatic credit card debit was
rejected owing to insufficient cleared funds."

That tells him what the problem is, and how to rectify it. As you say, it is
very difficult to do this portably. However it could be done - for instance,
we could specify a library function "stdouterrormessage()" that did just
that.
 
M

Malcolm

Joe Wright said:
Malcom, you have terminated the printf statement with ':' not ';'. Hence
no evidence of good programming.
I was working on a colon cancer dataset the other day, so what do you
expect?
 
O

Old Wolf

Well my planet has Lua, Java, Python, Perl, TCL, and bizarre new
languages like Haskel, Scheme, etc. They are all portable.

1975: Scheme
1987: Haskell
1990: Python
1991: Java
1993: Lua

More proof that your planet is totally ass-backwards.

BTW, see the excellent post by Walter Roberson on how
non-portable most Perl programs are.
 
K

Keith Thompson

Emmanuel Delahaye said:
jacob navia a écrit :

It is.

So what ? You don't even read r in your code.

What about

if (r < 0)
{
perror ("stdout);"
}

Sounds very portable to me.

Not quite. Apart from the syntax error (already mentioned
elsethread), printf() doesn't necessarily set errno to a meaningful
value on an error.
 
K

Keith Thompson

Emmanuel Delahaye said:
jacob navia a écrit :

no, because b and c are not initialized. The behaviour is undefined.

Presumably the "..." represents code in which b and c have values
assigned to them. jacob said it was just a snippet.
 
J

jacob navia

Malcolm said:
So what the user wants is

"This program could not complete because the disk is full. You have arranged
to purchase disk space on demand, but your automatic credit card debit was
rejected owing to insufficient cleared funds."

That tells him what the problem is, and how to rectify it. As you say, it is
very difficult to do this portably. However it could be done - for instance,
we could specify a library function "stdouterrormessage()" that did just
that.
Very funny.

What do you do with
fprintf(somefile,...);

1) You do not test the result. Anyway, a disk full is something never
happens, not to you anyway.
2) You code:
#ifdef LINUX
#ifdef TRIO_PRINTF
if (r == ...)
#elif GCC_PRINTF
if (r ==...)
#elif defined(WINDOWS)
#ifdef LCC_WIN32
if (r == ...)
#elif defined(WATCOM)
#elif defined (GCC)

#endif

etc etc, ad nauseum!

3) If there is an error you do a sloppy job
like assuming any error is a disk full error.

It is easy to show irony, much harder to discuss
correctly and understand what the other is
saying/proposing, and answering accordingly.

I wanted to show how difficult is to write anything really portable
because of the absence of error analysis standards in C. This is
due, as this discussion demonstrates, to the mentality "error
analysis is for wimps". Macho programmers never have any errors,
and anyway it is not worth thinking about this, since errors
are messy.

jacob
 
K

Keith Thompson

Emmanuel Delahaye said:
jacob navia a écrit :

Maybe you should try a better compiler :

Compiling: main.c
main.c: In function `main_':
main.c:10: warning: unknown conversion type character 0xa in format
main.c:10: warning: too many arguments for format

Sure, but the fact that it takes a "better compiler" to diagnose the
error reinforces the argument that it's not portable.
 
M

Mark McIntyre

etc etc
All practical C programs use pointers. Most Java programs don't need
threads.

etc etc

Guys, this is comp.lang.c not comp.lang.java...
How do you know you are not suffering from a mental illness?

Maybe he has a certificate. What do you care? Is it topical to CLC?
 
J

jacob navia

Eric said:
This would require that the Standard enumerate all
categories of errors -- maybe not every single possible
error, but at least all categories. Categorization involves
simplification: By lumping umpty-mumble errors into a single
category, you lose those errors' individual identities. Now,
you're evidently discontented with the ultimate simplification
and identity loss of "An error occurred," and would prefer to
discriminate more finely. Do you imagine that the level of
detail that satisfies you would satisfy everyone else?

Setting aside ranges rather than individual values solves
anything. You are (apparently) happy to lump all "I/O errors"
together, so long as they're distinct from "format errors."
Someone else might want to distinguish "logical I/O errors"
(disk full, security violation, ...) from "physical I/O errors"
(SCSI bus reset, modem hangup, ...), and this would require
subdividing your {MIN,MAX}_PRINTF_IOERROR range. And the next
guy would want to separate "real hardware errors" from "network
errors," and subdivide the subdivided ranges ... When you arrive
at the point where each range contains just one element, you're
back to enumerating every error again.

Since the errors are in a RANGE of numbers, and we have at least 16
bits in an int, we can easily make up ranges big enough to accomodate
for a LOT of error codes, 32767 in fact. The standard would define
several ranges, and leave a lot of logical eror code space unused
for the implementations to use.

It is absolutely not necessary for the standard to enumerate all
categories but the most probable ones: i/o and format errors, like
missing specifier for the conversion, etc. This two ranges would
suffice for most applications and would improve what is possible
in standard C...
 
G

Gordon Burditt

What, exactly, would replace each set of ellipses, and how
So what the user wants is

"This program could not complete because the disk is full. You have arranged
to purchase disk space on demand, but your automatic credit card debit was
rejected owing to insufficient cleared funds."

But banks do not give error messages like "debit was rejected owing
to insufficient cleared funds". They don't give much more detail
than "rejected", and that's probably a good thing, considering that
the error message is directed at someone other than the account
holder.

It is problematic assuming that if you have a disk, that it can be
expanded (at *ANY* cost). Problems include having maxed out your
configuration (no more slots or chassis can be added), all of the
disk manufacturing companies having gone out of business, inability
of the power grid to supply any more power to you, and local regulations
that limit the amount of fan noise pollution you are allowed to
generate.

Oh, yes, *credit cards* don't usually have cleared funds, *debit
cards* do, and you may not be able to get a bank to tell you which
kind the customer is trying to use in an error message, either.

Gordon L. Burditt
 
M

Michael Wojcik

Premise: any decently complex modern application cannot be entirely written
in standard C [meaning, the language without compiler added 'extensions',
and only using the standard libraries]. Or, if it is written in standard C,
then it is 'sub optimal' [and certainly doesn't have a nice UI!].

It's possible to write a number of "decently complex modern
application", at least as I'd define that phrase, in standard C.
Compilers can generally be written entirely in standard C; their
output may or may not be useful on multiple platforms, but the
programs themselves are portable.

I could write an interpreter for a scripting language entirely in
standard C. It would be limited to the facilities available to
standard C programs, and those that can be constructed upon them,
but that's quite a lot.

I could write an HTTP server entirely in standard C (I think - I
don't believe there's anything in RFC 2616 that would prevent that,
at least for a conditionally-compliant implementation), if it could
use stdio for its I/O, which is easy to do on Unix (using inetd).
And it could even have a GUI for administrative tasks (implemented
in HTML, Javascript, etc, and served up via HTTP), if that's what
you mean by "nice UI". (It's not what *I* would mean by "nice UI",
since I generally think GUIs and the like are a tremendous waste of
my time, but that's a different matter.)

And even if you want to exclude a system facility for doing network
I/O via stdio (which, in my opinion, would be changing the original
requirements), you'd still have an HTTP interpreter, which could be
very useful for such things as testing HTTP user agents.

Most applications use non-standard facilities, true. Probably most
of them benefit from them in some way. But that doesn't mean that
there aren't any interesting ones that can be written in standard C.

--
Michael Wojcik (e-mail address removed)

Pogo: The dogs *scarcely* ever catches the rabbit.
Bun: "Scarcely" got a very unpleasant ring of frequency to it.
-- Walt Kelly
 
E

Eric Sosman

Keith said:
Presumably the "..." represents code in which b and c have values
assigned to them. jacob said it was just a snippet.

Yes, but since the supposed non-portability depends
entirely on the elided material, it's a silly snippage.
That, I think, is Emmanuel's point.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,526
Members
44,997
Latest member
mileyka

Latest Threads

Top