new style declarations OK for old style definitions?

J

john

K&R/2 is a little vague on the following question: under what
circumstances is it legal to declare using new-style syntax a
separately compiled function that was compiled with an old-style
definition.

Is it sufficient to use only promoted arguments in the new-style
declaration?

Conceivably, the whole calling sequence for old style and new style
definitions could differ. Is it legal for the compiler to choose
completely incompatible calling sequences for old-style and new-style
declarations? It would certainly be desirable, since new-style
declarations seem to allow for significant optimizations that aren't
possible with old-style declarations.

This is a frequent problem if you have old library binaries and
sources but want the extra type checking from an ANSI compiler.

Thanks
 
E

Eric Sosman

K&R/2 is a little vague on the following question: under what
circumstances is it legal to declare using new-style syntax a
separately compiled function that was compiled with an old-style
definition.

Is it sufficient to use only promoted arguments in the new-style
declaration?

I believe so, but I can't find an explicit statement to that
effect in the Standard. There's a highly suggestive example in
6.2.7p5, but examples are non-normative. 6.5.2.2p6 addresses a
related but not identical situation, and 6.5.2.2p7 doesn't quite
nail the whole thing down.

As far as I know, there's no way to determine the type to
which an enum variable promotes (enum named constants are always
of type int, but an enum variable need not be; 6.7.2.2p4). And
there's probably no safe way to call an old-style variadic function;
if you prototype it with ,...) you'll get calls that are appropriate
for the <stdarg.h> machinery, whereas the old-style function is
likely using the non-portable pre-Standard <varargs.h>. Best bet
is probably to cross your fingers and hope the compiler never changes,
or to discard and rewrite the function.

One possibility, if you're really interested in policing things,
might be to write new-style wrappers for the old-style functions and
insist that people use the wrappers instead of making direct calls.
(Some `#define oldfunc %%% please use newfunc instead %%%' lines may
be helpful here.) This way, you can limit the scope of your worries
to the wrappers themselves rather than to an entire body of code.
Conceivably, the whole calling sequence for old style and new style
definitions could differ. Is it legal for the compiler to choose
completely incompatible calling sequences for old-style and new-style
declarations? It would certainly be desirable, since new-style
declarations seem to allow for significant optimizations that aren't
possible with old-style declarations.

As I said, I can't find an explicit prohibition. Still, it
seems unlikely that new- and old-style fixed-argument functions
would have different calling conventions. The only optimizations
I can think of would be avoiding needless promote-demote pairs
(e.g., float to double in the caller, double back to float in
the callee), but those should "fall out" of the extra knowledge
the prototype supplies.
This is a frequent problem if you have old library binaries and
sources but want the extra type checking from an ANSI compiler.

If you've got ancient binaries you may have additional trouble.
What I've written above applies to new- and old-style functions as
compiled by a modern compiler; a pre-Standard compiler for a pre-
Standard dialect of C may be doing a lot of surprising things ...
 
J

john

john said:
K&R/2 is a little vague on the following question: under what
circumstances is it legal to declare using new-style syntax a separately
compiled function that was compiled with an old-style definition.

Is it sufficient to use only promoted arguments in the new-style
declaration?

Conceivably, the whole calling sequence for old style and new style
definitions could differ. Is it legal for the compiler to choose
completely incompatible calling sequences for old-style and new-style
declarations? It would certainly be desirable, since new-style
declarations seem to allow for significant optimizations that aren't
possible with old-style declarations.

This is a frequent problem if you have old library binaries and sources
but want the extra type checking from an ANSI compiler.

Thanks

* bump *
 
J

john

Eric Sosman wrote:
< snip >

Thanks Eric.

Seeing as new C is backwards compatable, I sometimes wonder if it
wouldn't be easier just to write new code in K&R1 style and avoid all the
problems of inter-operability... I guess the ANSI standard buys you a few
nice features though.
 
S

Seebs

Seeing as new C is backwards compatable,

Mostly. Often.
I sometimes wonder if it
wouldn't be easier just to write new code in K&R1 style and avoid all the
problems of inter-operability... I guess the ANSI standard buys you a few
nice features though.

That would have been a great theory around 1987. Now, you should never
write anything in K&R1 style without a very specific reason to imagine that
you care about a compiler over twenty years old.

The argument checking, plus the ability to pass some types of arguments
at all, are big wins.

-s
 
E

Eric Sosman

Eric Sosman wrote:
< snip>

Thanks Eric.

Seeing as new C is backwards compatable, I sometimes wonder if it
wouldn't be easier just to write new code in K&R1 style and avoid all the
problems of inter-operability... I guess the ANSI standard buys you a few
nice features though.

... for example, the prototypes you're struggling to write! IMHO,
the greatest benefit of the C89/C90 Standard was that it provided a
language definition that amounted to more than "Whatever my compiler
does is right." That pre-Standard definition led to divergent dialects,
greater porting expenses, and "Our way is better" from pretty much all
compiler suppliers. The Standard scotched most of these arguments and
gave all suppliers a common target to aim at -- there remain the areas
the Standard deliberately doesn't cover or governs only in part, and the
areas where the Standard's language is open to misunderstanding, but by
and large I feel the single greatest benefit of the Standard was, well,
standardization.

Prototyped function definitions and declarations, though, run a
close second in the adding-value sweepstakes. In the Bad Old Days it
was depressingly common to find `int f(a,b) int a,b; {...}' called
as `f(42)'. Quite often the error would go undetected even at run time
if a plausible `b' value just happened to be in the right register or at
the right stack offset or whatever -- until you cranked up the optimizer
and all Hell broke loose, or until you upgraded the compiler and ditto,
or until you merely changed a few addresses by re-linking and ditto, or
(most especially) when you ported the code to another machine and heard
the bells of Hell peal for sheer joy of deviltry. Function prototypes
are a *huge* benefit and cost-saver; they're "nice features" the same
way polio vaccine is a "nice feature."
 
K

Keith Thompson

john said:
john wrote:
[question snipped]

* bump *

It looks like you posted a followup, quoting your original question
and adding only "* bump *", in an effort to raise the visibility
of your question. You did this less than an hour after you posted.

Please don't do that here. I've seen web forums where that kind
of thing makes sense, but this is a newsgroup. Generally the
set of articles that are visible to a given reader is specific to
that reader, based on what the reader has already read and on the
reader's personal preferences. Trying to mess with that is likely
to be counterproductive.
 
T

Tim Rentsch

john said:
K&R/2 is a little vague on the following question: under what
circumstances is it legal to declare using new-style syntax a
separately compiled function that was compiled with an old-style
definition.

Is it sufficient to use only promoted arguments in the new-style
declaration?

The relevant paragraph is 6.7.5.3p15. Note in particular the
second-to-last sentence.

For two function types to be compatible, both shall specify
compatible return types(127). Moreover, the parameter type
lists, if both are present, shall agree in the number of
parameters and in use of the ellipsis terminator; corresponding
parameters shall have compatible types. If one type has a
parameter type list and the other type is specified by a function
declarator that is not part of a function definition and that
contains an empty identifier list, the parameter list shall not
have an ellipsis terminator and the type of each parameter shall
be compatible with the type that results from the application of
the default argument promotions. If one type has a parameter
type list and the other type is specified by a function
definition that contains a (possibly empty) identifier list, both
shall agree in the number of parameters, and the type of each
prototype parameter shall be compatible with the type that
results from the application of the default argument promotions
to the type of the corresponding identifier. (In the
determination of type compatibility and of a composite type, each
parameter declared with function or array type is taken as having
the adjusted type and each parameter declared with qualified type
is taken as having the unqualified version of its declared type.)

with footnote (127):

If both function types are ``old style'', parameter types are not
compared.

Conceivably, the whole calling sequence for old style and new style
definitions could differ.

Under ISO C they cannot. As long as the declaration (with prototype)
is compatible with the type at the definition -- regardless of whether
it is "old style" or "new style" -- the declaration is legal and calls
matching the declaration must work, no matter how the function is
defined, and since the compiler doesn't know how it's defined, the
calling sequences for the two cases must be the same.
Is it legal for the compiler to choose
completely incompatible calling sequences for old-style and new-style
declarations?

Only in the sense that it's possible to jigger up all sorts of strange
things under the "as if" rule. Practically speaking, no compiler
would ever go to such lengths, because calls in the two cases must
behave identically. Not only that, but taking the address of a
function using '&' produces a pointer-to-function that must behave
identically for the two cases. There is just no reasonable way to
do this in a standard compilation environment where the function in
question is defined in another translation unit.

It would certainly be desirable, since new-style
declarations seem to allow for significant optimizations that aren't
possible with old-style declarations.

I guess it's possible that this is right, but it's the wrong question.
The question is, if we have a new style _declaration_, does it make
sense to use different calling sequences for a new style _definition_
vs an old style _definition_. The new style _declaration_ gives us
all the information we need to do any possible optimizations in both
cases. At the point of _definition_, there is enough information
available to construct a new style _declaration_, and that allows
all the optimizations that would be possible if the definition were
written using a prototype.

Remember: functions defined with prototypes (assuming they meet
certain criteria as regards which types are present, use of ellipsis,
etc) must be callable using either new style or old style declarations.
Because we don't know (in those cases that meet the criteria) whether
the declaration style matches the definition style, and all four cases
must work the same way, there's no useful way to differentiate in
general.

This is a frequent problem if you have old library binaries and
sources but want the extra type checking from an ANSI compiler.

If you're compiling the (old style) sources, a good sanity check
is to write new-style declarations (ie the same ones that you're
using to compile the other code) and arrange to have those be
present in the source before the old style function definitions.
(Can be done as compilations "on the side" if necessary.) This
way, if the prototypes don't match the definitions, the compiler
will squawk, and you'll know the prototype is no good. But if
the old style definition is accepted with a prototype declared
before it, then the old style definition is going to work when
called using that prototype declaration.
 
N

Nick Keighley

Seeing as new C is backwards compatable, I sometimes wonder if it
wouldn't be easier just to write new code in K&R1 style and avoid all the
problems of inter-operability... I guess the ANSI standard buys you a few
nice features though.

do you actually have some K&R code you have to interface with?
 

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,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top