Inconsistent argument order in stdlib functions

F

Francine.Neary

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.

Now this can quite easily be corrected by macros, for example:

#include <stdio.h>

#define fputs(X,Y) fputs(Y,X)

main() /* note: returning int not void!! */
{
fprintf((FILE *) stdout, "A test\n");
fputs((FILE *) stderr, "Another test\n"); /* consistency reigns! */
return 0;
}

So basically I was planning to build up a list of macro definitions to
regulate whatever inconsistencies I come through in stdlib, then I can
put them in a header to be #included in all my programs. But surely
other people will already have done this, so I thought I'd ask if any
such list is available somewhere before reinventing the wheel.
 
B

Ben Pfaff

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.

A good rule of thumb is that the FILE pointer comes last, except
for functions with a variable number of arguments, in which case
it comes first.
Now this can quite easily be corrected by macros, for example:

#include <stdio.h>

#define fputs(X,Y) fputs(Y,X)

I would recommend doing that. For one thing, the implementation
is allowed to declare a macro from each standard library
function. For another, you'll confuse any C programmer who reads
your code.
 
E

Eric Sosman

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.

Yes, it's inconsistent, but with practice you'll find
it is not hard to remember. Not for the functions you use
frequently, anyhow: I confess that despite thirty-plus years
of programming in C the order of the arguments to bsearch()
still eludes me (I scarcely ever use it), and I still get
confused about the middle two arguments to fread() and
fwrite(). But that's never bothered me much, because on
the occasions when I want to use one of these functions I
just look them up in any convenient reference: man page,
info screen, or even (gasp!) a book printed on paper. It's
not difficult.

To understand the reason for the inconsistencies, you
must realize that the C library did not spring full-armed
from the brow of Jove. It was not invented by one person,
at one place, or even at one time, but evolved over a span
of years at many places, with people borrowing ideas they'd
seen elsewhere and "improving" on them in assorted (sometimes
incompatible) ways. The library as it stands today is a
fusion of multiple different libraries with various styles
and various levels of rigor.

Evolution is like that -- or haven't you thought about
your vermiform appendix lately?
Now this can quite easily be corrected by macros, for example:

#include <stdio.h>

#define fputs(X,Y) fputs(Y,X)

You need to start with `#undef fputs' to guard against
the possibility that <stdio.h> has already defined `fputs'
as a macro.

But oh, dear Lord, sweet Jesus, don't do this, I beg!
If you simply *must* introduce macros to arrange things to
your liking, at least give them names that don't look like
something else! Your code will be completely unreadable,
and (you will discover) unreadable code tends to acquire
an unusually high density of bugs -- and then turn out to
be unmaintainable. When you write code you will be writing
in a private language nobody else understands -- and you
will also find your own ability to read the common language
diminished. If you cannot live without your silly macro,
at least have the decency to call it `BFTSPLX'.
main() /* note: returning int not void!! */

("But not for long!" -- Sweeney Todd) Under the new
"C99" version of the Standard, it is illegal to omit the
type. Typing "eye enn tee space" saves keystrokes, compared
to the comment you had to type to defend its omission.
{
fprintf((FILE *) stdout, "A test\n");

Useless cast, since `stdout' is already a `FILE*'. Or,
to put it another way, why didn't you write

fprintf((FILE *)stdout, (char*)"A test\n");
fputs((FILE *) stderr, "Another test\n"); /* consistency reigns! */

Again, `stderr' is already a `FILE*'.
return 0;
}

So basically I was planning to build up a list of macro definitions to
regulate whatever inconsistencies I come through in stdlib, then I can
put them in a header to be #included in all my programs. But surely
other people will already have done this, so I thought I'd ask if any
such list is available somewhere before reinventing the wheel.

Nobody in his right mind has done such a thing, I am sure.
Invent your wheel if you must; I am sure it will roll you
straight to Perdition.

"A foolish consistency is the hobgoblin of little minds,
Adored by little statesmen and philosophers and divines."
-- Ralph Waldo Emerson

"That leg's long enough; pull on the other."
-- Anonymous
 
N

Nelu

Ben said:
(e-mail address removed) writes:

I would recommend doing that. For one thing, the implementation
is allowed to declare a macro from each standard library
function. For another, you'll confuse any C programmer who reads
your code.

Is that *wouldn't*?
 
S

Servé Laurijssen

Eric Sosman said:
You need to start with `#undef fputs' to guard against
the possibility that <stdio.h> has already defined `fputs'
as a macro.

2 questions pop up.

1. Creating a macro with the same name as an already existing function is
not undefined behaviour?
2. Is it possible for implementations to offer fputs as a macro only or must
it always be a real function too?
 
K

Keith Thompson

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.

Now this can quite easily be corrected by macros, for example:

#include <stdio.h>

#define fputs(X,Y) fputs(Y,X)

main() /* note: returning int not void!! */

If main returns int (which it does), you should say so. Implicit int
was never a particularly good idea, and it's been removed from the
language in C99. Declare it as:

int main(void)
{
fprintf((FILE *) stdout, "A test\n");

stdout is already of type FILE*. There's no need to cast it.

*Most* casts are unnecessary.
fputs((FILE *) stderr, "Another test\n"); /* consistency reigns! */
return 0;
}

So basically I was planning to build up a list of macro definitions to
regulate whatever inconsistencies I come through in stdlib, then I can
put them in a header to be #included in all my programs. But surely
other people will already have done this, so I thought I'd ask if any
such list is available somewhere before reinventing the wheel.

You can do that if you really want to, but *please* don't redefine the
standard functions with the same names. Anyone reading your code
who has managed to learn the correct order of the arguments to fputs()
is going to see your
fputs(stderr, "Another test\n");
and be convinced that it's an error. For that matter, I think that
redefining standard function names like this invokes undefined
behavior.

If you really want to do this, pick your own names, preferably with a
unique prefix.

I'm not sure anyone has bothered to do this, and I suspect it's
because most programmers don't think it's necesssary. A programmer
who's experienced enough to implement such a set of macros most likely
has gotten used to the odd parameter orderings and either has
memorized them or keeps a reference book at hand to look them up when
necessary. Another common way to avoid the confusion is to use just,
say, printf() and fprintf() rather than fputs:

fprintf(stderr, "Another test\n");
fprintf(stderr, "%s\n", message);

This has some cost in performance, but that's usually not significant.
 
E

Eric Sosman

Servé Laurijssen wrote On 03/09/07 14:48,:
2 questions pop up.

1. Creating a macro with the same name as an already existing function is
not undefined behaviour?

No. It may look at first like an infinite recursion,
but macros are not expanded recursively. That is, if the
expansion of macro M generates another appearance of M,
directly or indirectly, the generated M is not treated as
a macro.
2. Is it possible for implementations to offer fputs as a macro only or must
it always be a real function too?

It must be provided as a real function, primarily so
that you can aim a function pointer at it:

#include <stdio.h>
int (*fptr)(const char *, FILE *) = fputs;
...
fptr("Hello, world!\n", stdout);

This is the case with most things in the Standard library;
a very few (like assert(), for example) are required to be
macros and only macros.

However, the implementation is allowed to provide macros
for any of the Standard "real functions" that it likes. This
is usually done in the name of efficiency: putc(), for example,
is frequently implemented as a macro expanding to in-line code
that usually avoids the overhead of a function call. Some
implementations use special macros for some of the mathematical
and string-bashing functions; <math.h> might contain something
along the lines of

double sqrt(double);
#define sqrt(x) __emit_sqrt_instruction_in_code__(x)

... thus providing both an ordinary function you can point at
with a function pointer, and a (presumably) fast alternative
for in-line use.
 
B

Ben Pfaff

Servé Laurijssen said:
2. Is it possible for implementations to offer fputs as a macro only or must
it always be a real function too?

There always has to be a function definition. It's perfectly
valid to do
#undef fputs
and then use fputs. Or you can call the function as
(fputs)(...), which doesn't get expanded.
 
C

CBFalconer

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.

Think of it this way - the file id goes on the end, if possible.
For a variadic function the end is unknown, so the file id goes at
the beginning. If you follow this rule for your own FILE handling
functions there will be no confusion.

I suspect the original reason has to do with the convenience of
writing the actual functions, combined with the original C practice
of pushing arguments in reverse order. Thus the non-variadic
functions want to do all their manipulations, and only at the end
need to access the deeply buried file id.

Note that these are neither ids nor handles. They are FILE*
pointers to a stream control block of some form or other.
 
F

Francine.Neary

To understand the reason for the inconsistencies, you
must realize that the C library did not spring full-armed
from thebrow of Jove.
<snip>

Thanks for the humorous explanation, and to Ben Pfaff for a nice
mnemonic.
But oh, dear Lord, sweet Jesus, don't do this, I beg!
If you simply *must* introduce macros to arrange things to
your liking, at least give them names that don't look like
something else! Your code will be completely unreadable,
and (you will discover) unreadable code tends to acquire
an unusually high density of bugs -- and then turn out to
be unmaintainable. When you write code you will be writing
in a private language nobody else understands -- and you
will also find your own ability to read the common language
diminished. If you cannot live without your silly macro,
at least have the decency to call it `BFTSPLX'.

How about Fputs then?
("But not for long!" -- Sweeney Todd) Under the new
"C99" version of the Standard, it is illegal to omit the
type. Typing "eye enn tee space" saves keystrokes, compared
to the comment you had to type to defend its omission.

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.
Useless cast, since `stdout' is already a `FILE*'

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. */
)
Nobody in his right mind has done such a thing, I am sure.
Invent your wheel if you must; I am sure it will roll you
straight to Perdition.

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.
 
K

Keith Thompson

Eric Sosman said:
Servé Laurijssen wrote On 03/09/07 14:48,:

No. It may look at first like an infinite recursion,
but macros are not expanded recursively. That is, if the
expansion of macro M generates another appearance of M,
directly or indirectly, the generated M is not treated as
a macro.
[...]

I was about to say that fputs is reserved, not because of the infinite
recursion problem, but because it's defined in a standard header. But
fortunately I checked the standard first. C99 7.1.3:

All identifiers with external linkage in any of the following
subclauses (including the future library directions) are always
reserved for use as identifiers with external linkage.

This includes "fputs". Since macros don't have external linkage, you
can legally define a macro "fputs" (if you #undef it first).

But this is one of those cases where an identifier is reserved for
some purposes, but it's easier just to avoid redeclaring or redefining
it altogether. There are some moderately complex rules about which
identifiers are reserved for what; I prefer to pretend that all such
identifiers are reserved for all purposes. For example, I avoid all
identifiers that start with an underscore, even though some of them
are permitted for some uses.

The proposed fputs() macro with the arguments swapped is an example of
why such redefinitions are a bad idea. The compiler may be required
to let you get away with it, but it's still wise to show some mercy to
the next person who reads or maintains your code -- even if that's
only you.
 
F

Francine.Neary

I would recommend doing that. For one thing, the implementation
is allowed to declare a macro from each standard library
function.

Wouldn't a modern C preprocessor expand macros recursively?
 
G

Guest

Eric said:
Servé Laurijssen wrote On 03/09/07 14:48,:

No. It may look at first like an infinite recursion,
but macros are not expanded recursively. That is, if the
expansion of macro M generates another appearance of M,
directly or indirectly, the generated M is not treated as
a macro.

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.
 
C

CBFalconer

Ben said:
(e-mail address removed) writes:
.... snip ...

I would recommend doing that. For one thing, the implementation ^^^
is allowed to declare a macro from each standard library
function. For another, you'll confuse any C programmer who reads
your code.

I suspect an elided 'not'.
 
E

Eric Sosman

[... large snip; in re using macros to permute the arguments
of standard functions, e.g. #define fputs(x,y) fputs(y,x) ...]
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.

Try an experiment. Construct a sign that reads

<--- RIGHT

LEFT --->

and attach it to the rear of your car where everyone can
see it easily so there'll be no confusion. Also, find the
wiring harness and exchange the cables that lead to your
car's directional signals, so they'll be consistent with
your new nomenclature. Let us know how things work out.

(This procedure would make you a perfectly normal
Boston driver, if it weren't for the sign ...)
 
G

Guest

Keith said:
Eric Sosman said:
Servé Laurijssen wrote On 03/09/07 14:48,:

No. It may look at first like an infinite recursion,
but macros are not expanded recursively. That is, if the
expansion of macro M generates another appearance of M,
directly or indirectly, the generated M is not treated as
a macro.
[...]

I was about to say that fputs is reserved, not because of the infinite
recursion problem, but because it's defined in a standard header. But
fortunately I checked the standard first. C99 7.1.3:

All identifiers with external linkage in any of the following
subclauses (including the future library directions) are always
reserved for use as identifiers with external linkage.

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.
 
C

CBFalconer

Eric said:
(e-mail address removed) wrote On 03/09/07 13:24,:
.... snip ...

Nobody in his right mind has done such a thing, I am sure. Invent
your wheel if you must; I am sure it will roll you straight to
Perdition.

"A foolish consistency is the hobgoblin of little minds,
Adored by little statesmen and philosophers and divines."
-- Ralph Waldo Emerson

"That leg's long enough; pull on the other."
-- Anonymous

See sig.
 
S

Stephen Sprunk

Keith Thompson said:
Another common way to avoid the confusion is to use just,
say, printf() and fprintf() rather than fputs:

fprintf(stderr, "Another test\n");
fprintf(stderr, "%s\n", message);

This has some cost in performance, but that's usually not significant.

A decent compiler will replace fprintf() calls with fputs() calls when it
can do so safely (ditto for printf() and puts()), such as in the cases
above. There's no reason to use fputs() at all these days. Let the
compiler do the work for you, and don't mess with tiny, premature
optimization steps unless you can prove it's not doing a good enough job.

S
 

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,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top