Inconsistent argument order in stdlib functions

K

Keith Thompson


*Please* don't snip attribution lines (lines of the form
discussion easier to follow, and giving proper credit is just polite.

[...]
How about Fputs then?

If you must do it, I suppose Fputs is a decent name. Some very old
linkers don't necessarily distinguish between upper and lower case for
external names, but I don't *think* that's likely to be a problem in
practice.

But I'm hesitant to offer too much advice on what to call these
things, since I think they're a bad idea in the first place. The
rules really aren't all that complex. You'll likely spend less time
learning them than you'd spend implementing your aliases -- and as a
result, you'll be better able to read other people's code (because
others aren't going to use your aliases), and other people will be
better able to read your code.
Normally I wouldn't put a comment - I think having int as the default
return type really helps streamline code. The comment was because
several people from this group recently expressed to me a strong
preference for main returning int not void, so I wanted to emphasize
that I'm now following their advice.

Bravo. But the point is that you only followed *part* of their
advice. Leaving out the word "int" is allowed (in C90, but not in
C99), but you gain nothing by doing so. Leaving out the word "void"
is also allowed, but again, you gain nothing by doing so. Change it
from "main()" to "int main(void)", and get into the habit of writing
it that way. Trust me on this; you'll be happier in the long run.
My stdio.h implements std(in|out|err) as macros, so I thought it best
to be on the safe side and typecast. (In fact there's a funny comment
about that in stdio.h:
/* C89/C99 say they're macros. Make them happy. */
)

Yes, the standard requires stdin, stdout, and stderr to be macros. It
also requires those macros to expand to expressions of type FILE*.
There is no realistic chance that an implementation will get this
wrong. If one did, it's not likely that casting ("typecast" isn't a
word unless you're talking about actors) will do any good.

[...]
Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.

Yes, it really does seem like such an outlandish thing. How is
someone reading your code going to know where your macro definitions
are? Presumably they'd be in some header you've written, but where is
it? Did you put it in the same directory as the source file that uses
it, or is it in a separate "include" directory, or is it off somewhere
else altogether?

And if I have to track down your macro definition to figure out what
Fputs(foo, bar) means, that tells me that it really means
fputs(bar, foo) -- but I *still* have to know that fputs() expects a
char* followed by a FILE*.

I can certainly understand the temptation to try to turn C into
something just a little bit better. The original Bourne shell under
Unix was originally written using a set of macros that made the
source look Pascalish:

#define BEGIN {
#define END }
#define IF if (
#define THEN )
#define ELSE } else {

The result was nearly illegible both to C programmers and to Pascal
programmers.

I like some languages better than others, but I generally try to
accept each language on its own terms.

In the 8th of Henry Spencer's "The Ten Commandments for C
Programmers", he wrote that "thy creativity is better used in solving
problems than in creating beautiful new impediments to understanding".
 
K

Keith Thompson

Harald van Dijk said:
That's correct. However, define a macro with the same name as a
standard library function whose header is included, and the behaviour
is undefined. Even if you undefine any pre-existing macro first, and
even if the compiler would need special magic to have any problem with
your custom definition.

I don't *think* that's correct. See the rules for reserved
identifiers in 7.1.3; I think fputs, for example, is reserved only for
use as an identifier with external linkage.

But re-using the name "fputs" is A Bad Idea, regardless of whether
it's strictly allowed. The only good excuse I can think of for doing
so would be to work around a buggy implementation. (Using the
parameter order specified by the standard is not something I consider
to be a bug.)
 
K

Keith Thompson

Harald van Dijk said:
Keith Thompson wrote: [...]
This includes "fputs". Since macros don't have external linkage, you
can legally define a macro "fputs" (if you #undef it first).

7.1.3 says more than just that:

p1:
Each identifier with file scope listed in any of the following
subclauses (including the future library directions) is reserved for
use as a macro name and as an identifier with file scope in the same
name space if any of its associated headers is included.

p2:
If the program declares or defines an identifier in a context in which
it is reserved (other than as allowed by 7.1.4), or defines a reserved
identifier as a macro name, the behavior is undefined.

So fputs is reserved for use as a macro name (regardless of whether
you undefined it), and even if it wasn't, you still wouldn't be
allowed to define it as a macro yourself.

Ok, you're right. (I was incorrectly thinking of "external linkage"
and "file scope" as exclusive properties; clearly they aren't.)
 
K

Keith Thompson

Keith Thompson said:
(e-mail address removed) writes: [...]
Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.

Yes, it really does seem like such an outlandish thing.
[snip]

Incidentally, if you're feeling attacked, please don't. It might seem
like everyone is piling on to tell you repeatedly that you're wrong,
Wrong, WRONG!!, but it's nothing personal. The idea you're presenting
is actually a fairly interesting one, and I've learned a thing or two
in the course of discussing it. (I'm not changing my position, just
clarifying the context of the discussion.)
 
K

Keith Thompson

Keith Thompson said:
I don't *think* that's correct. See the rules for reserved
identifiers in 7.1.3; I think fputs, for example, is reserved only for
use as an identifier with external linkage.

Harald was right; I was wrong.
 
E

Eric Sosman

Keith said:
Keith Thompson said:
(e-mail address removed) writes: [...]
Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.
Yes, it really does seem like such an outlandish thing.
[snip]

Incidentally, if you're feeling attacked, please don't. It might seem
like everyone is piling on to tell you repeatedly that you're wrong,
Wrong, WRONG!!, but it's nothing personal. The idea you're presenting
is actually a fairly interesting one, and I've learned a thing or two
in the course of discussing it. (I'm not changing my position, just
clarifying the context of the discussion.)

And (as one who might be viewed as an attacker), I confess
similar urges in my younger days. For a mercifully brief while,
I had near the top of many of my source files

#define until(x) while(!(x))

(Maybe you can imagine why.) It didn't take an absurdly long
time for this impulse to fade; perhaps yours, too, will be
similarly and mercifully short-lived.

No one believes C is the perfect language, and as Young
Turks we all go through a phase of believing we can improve on
it. With time comes the wisdom to understand (and accept) that
the blemishes have their own beauty, and that that some of the
wisdom built into C would not survive "improvement."
 
K

Keith Thompson

Eric Sosman said:
Keith said:
Keith Thompson said:
(e-mail address removed) writes: [...]
Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.
Yes, it really does seem like such an outlandish thing.
[snip]
Incidentally, if you're feeling attacked, please don't. It might
seem
like everyone is piling on to tell you repeatedly that you're wrong,
Wrong, WRONG!!, but it's nothing personal. The idea you're presenting
is actually a fairly interesting one, and I've learned a thing or two
in the course of discussing it. (I'm not changing my position, just
clarifying the context of the discussion.)

And (as one who might be viewed as an attacker), I confess
similar urges in my younger days. For a mercifully brief while,
I had near the top of many of my source files

#define until(x) while(!(x))

(Maybe you can imagine why.) It didn't take an absurdly long
time for this impulse to fade; perhaps yours, too, will be
similarly and mercifully short-lived.

No one believes C is the perfect language, and as Young
Turks we all go through a phase of believing we can improve on
it. With time comes the wisdom to understand (and accept) that
the blemishes have their own beauty, and that that some of the
wisdom built into C would not survive "improvement."

And then there are a few people, like Bjarne Stroustrup (inventor of
C++), Brad Cox and Tom Love (the inventors of Objective C), and a few
others, who actually go on to invent new languages that are intended
to be better versions of C. Doing this right takes a *lot* of work.
Rule One: You can't change just one thing. (Whether Stroustrup and
company are brilliant language designers or just Young Turks who
didn't know when to quit is another question, one that I urge everyone
here to leave unanswered.)
 
S

Servé Laurijssen

Keith Thompson said:
And then there are a few people, like Bjarne Stroustrup (inventor of
C++), Brad Cox and Tom Love (the inventors of Objective C), and a few
others, who actually go on to invent new languages that are intended
to be better versions of C. Doing this right takes a *lot* of work.
Rule One: You can't change just one thing. (Whether Stroustrup and
company are brilliant language designers or just Young Turks who
didn't know when to quit is another question, one that I urge everyone
here to leave unanswered.)

In his book "The design and evolution of C++" you will learn that C++ never
started out to "improve on C". It started out as a low level language with
classes added as in Simula. Just that at some point a decision had to be
made on the syntax and C seemed the best choice at that time. You're
presenting it here as if the problem solved by C++ was that "C is crappy"
but that wasnt the case at all. The problem solved was "we dont have a low
level language with which you can design programs as how you think of them
as in the real world" (classes)
 
K

Keith Thompson

Servé Laurijssen said:
In his book "The design and evolution of C++" you will learn that C++ never
started out to "improve on C". It started out as a low level language with
classes added as in Simula. Just that at some point a decision had to be
made on the syntax and C seemed the best choice at that time. You're
presenting it here as if the problem solved by C++ was that "C is crappy"
but that wasnt the case at all. The problem solved was "we dont have a low
level language with which you can design programs as how you think of them
as in the real world" (classes)

I'll have to take your word for that; it's been a long time since I
read that book. It's off-topic, so I won't get into it any further.
 
S

SM Ryan

(e-mail address removed) wrote:
# I'm finding it really hard to keep some of these things straight...
# For example, fputs is to puts as fprintf is to printf, except that
# fputs has the file handle at the end and fprintf at the beginning!
# Very illogical! And hard to remember.

Yeah, sucks sometimes.

# Now this can quite easily be corrected by macros, for example:
#
# #include <stdio.h>
#
# #define fputs(X,Y) fputs(Y,X)

This will fail if fputs is a function. What you need to do
in this case is
#define fputs(X,Y) (fputs)(Y,X)
This will also fail if fputs is a #define.

Overall it's safer to come up with your own names so there's
no danger of conflict.
#define wts(X,Y) (fputs(Y,X))
#define wtc(X,Y) (fputc(Y,X))
...

If your compiler supports inline, it's even better to use that
inline int wts(FILE *f,char *s) {
return fputs(s,f);
}
since you get typechecking and you can define variables and
use iterations inside your function.
 
F

Francine.Neary

Incidentally, if you're feeling attacked, please don't. It might seem
like everyone is piling on to tell you repeatedly that you're wrong,
Wrong, WRONG!!, but it's nothing personal. The idea you're presenting
is actually a fairly interesting one, and I've learned a thing or two
in the course of discussing it. (I'm not changing my position, just
clarifying the context of the discussion.)

No, not at all. I'm very grateful for the advice people have given me.
In this thread, several people have argued very persuasively that my
idea for reordering arguments is a flawed one, and I'm now convinced
that the right thing to do is stick to the standard order, consulting
man pages as necessary.
 
O

Old Wolf

Try an experiment. Construct a sign that reads

<--- RIGHT

LEFT --->

Might not be as bad as you think. My work recently moved into a new
office and we have a sign laid out just like that, except RIGHT was
our name and LEFT was another company in the same building. However,
we kept getting arrivals for the other company! Eventually someone
figured out that the arrivals were associating the written name with
the arrow above/below it, rather than the one beside it. We moved our
sign up and to the side, and have had far fewer misdirected arrivals
since.
 
O

Old Wolf

My stdio.h implements std(in|out|err) as macros, so I thought it best
to be on the safe side and typecast.

How is that safe? If you think it is safe then you must have some
misconceptions about C (so it would be good to sort them out now
rather than later).

If anything, it is unsafe, e.g. if you had a variable elsewhere:
int stout;

and accidentally made a typo:
fprintf((FILE *) stout, "A test\n");

then you may not get a compiler warning, where you would have got one
without the cast.
 
G

Giorgos Keramidas

[regarding redefinition of fputs() to fputs(FILE *, const char *)]

Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.

Yes, it does seem such an outlandish thing. It only serves your current
purpose. Later down, you will have to be *extremely* careful about the
header which includes this redefinition of fputs(). If some unlucky
soul happens to include your magic header in a C source file which
abides by the standard order of fputs() arguments, all sorts of
``interesting'' bugs will start manifesting their extremely funny idea
of how your program should crash.
 
E

Eric Sosman

Giorgos said:
[regarding redefinition of fputs() to fputs(FILE *, const char *)]

Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.

Yes, it does seem such an outlandish thing. It only serves your current
purpose. Later down, you will have to be *extremely* careful about the
header which includes this redefinition of fputs(). If some unlucky
soul happens to include your magic header in a C source file which
abides by the standard order of fputs() arguments, all sorts of
``interesting'' bugs will start manifesting their extremely funny idea
of how your program should crash.

s/crash/fail to compile/

Still, it remains an outlandish thing.
 
R

Robert Gamble

Giorgos said:
[regarding redefinition of fputs() to fputs(FILE *, const char *)]
Hmmm, does it really seem like such an outlandish thing? After all if
another programmer does read my code he'll have easy access to any
macro definitions, so that shouldn't cause too much difficulty.
Yes, it does seem such an outlandish thing. It only serves your current
purpose. Later down, you will have to be *extremely* careful about the
header which includes this redefinition of fputs(). If some unlucky
soul happens to include your magic header in a C source file which
abides by the standard order of fputs() arguments, all sorts of
``interesting'' bugs will start manifesting their extremely funny idea
of how your program should crash.

s/crash/fail to compile/

Unless the arguments are passed as void pointers, contrived but
possible.

Robert Gamble
 

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,776
Messages
2,569,603
Members
45,186
Latest member
vinaykumar_nevatia

Latest Threads

Top