From: jimad@microsoft.com (Jim Adcock)
Subject: Re: Standard library functions and macros
Date: 29 Dec 92 22:24:53 GMT
Organization: Microsoft Corporation

In article <DAVEG.92Dec24193847@synaptx.synaptics.com> daveg@synaptics.com (Dave Gillespie) writes:
|In article <1992Dec23.214139.24948@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
|I have to disagree here.  Usually when language standards talk about
|the "meaning" of a program, they are referring only to whether or not
|it is correct, and what its behavior will be if it is correct.  As I
|understand it, all the talk about macros in the C standard is there
|purely to reserve to implementors the right to provide fast macro
|implementations of library functions.

Again, section 4.1.6 of X3.159-1989 calls out explicit rights not
only of implementors, but also of programmers.  Implementors have
the right to use macro implementations, but also have to provide
function implementations "in the background."  Programmers have the
right to bypass the macro implementation [if any] using the "background" 
function-call implementation. The ANSI-C++ committee intends to
usurp both the rights of the implementor and the rights of the 
programmer on this matter.  Why?  There is NO REASON why the ANSI-C++
committee has to use the ANSI-C libraries if they don't like the ANSI-C
library rules.  Let the ANSI-C++ committee define their own libraries,
with the own *differently named include headers*, if they do not like
the way the ANSI-C libraries are defined to work.  That way the
programmer could choose whose set of rules they agreed with --
ANSI-C or ANSI-C++ -- and more importantly existing historical 
agreements between C implementors and C programmers would not be broken.

For example, the ANSI-C++ committee could decide they know better than
ANSI-C how the ANSI-C headers ought to work, could name THEIR version
of the header files <stdio.h++> [or something] and then the programmer
could decide WHOSE header and set of rules to follow <stdio.h> or
<stdio.h++>

I am not saying this is what SHOULD be done, I'm just saying there
is no excuse for the ANSI-C++ committee to REQUIRE changes to the
rules of how the ANSI-C libraries and their associated headers work.
Because ANSI-C++ could simply NAME their headers differently if they
are going to require that their headers WORK differently!

|If the only effect of `#undef putchar' is to change whether or not
|a fast macro or a slow equivalent function is used, i.e., if it only
|affects performance and not results, then I'd say it's improper for
|a standard to legislate it one way or another, or for a portable
|program to depend on it.

I agree.  Thus I am lobbying against the proposed ANSI-C++ mandated
change.  If the ANSI-C++ doesn't mandate the implementation, then
implementors can *continue* to implement standard C functions using
macros.  Portable programs [ie "strictly conforming"] won't depend on this.
But non-portable but still conforming programs CAN and DO depend upon
this.  Remember, the vast majority of programs ARE NOT written to
be portable, and it was never the intent of ANSI-C to *require*
portability, but rather to *allow* portability to people who were
so motivated.  On the contrary, ANSI-C clearly states that they
consider *speed* a more important criteria than portability, and
that programmers should not be hindered from writing fast, non-portable,
programs.  Requiring inline implementation would be such a hindrance, because
the reality remains that in some instances macros can still be
faster than inlines.

|Even if `putchar' were a function instead of a macro, don't compilers
|sometimes inline-expand functions anyway?  A good optimizing compiler
|ought to be able to do this, whether or not there are "inline"
|declarations in the language.  As far as I know, there is no way in
|C to require that a function be compiled out-of-line.  The only
|language I can think of offhand that does provide for this is Common
|Lisp.

Various implementation have many different capabilities and implementations.
What I am lobbying against is the proposed ANSI-C++ change requiring
implementors NOT to use one historical implementation choice, namely
the new proposed restriction requiring implementations to no longer
use macro implementations of historical C standard functions.  Even
though those macro implementations are clearly permitted and intended as
stated in the ANSI-C standard.

|I don't have a copy of the standard handy, but my K&R II uses language
|like "if it is a macro" in its description of "putchar".  Doesn't that
|imply that it is perfectly legal for a compiler to implement `putchar'
|as a function, not a macro?

Yes, and it implies that it is perfectly legal for a compiler to
implement it as a macro, not a function.  And it is perfectly legal
for a conforming program to expect its given implementation to provide
this function in macro form, and to require that particular implementation
for the "success" of that particular program.  Strictly-conforming
programs are not allowed to have expectations on implementation beyond
that called out in ANSI-C.  Conforming programs can have any expectations
on a particular implementation that they want.  Such expectation represent
private agreement between an implementor and a programmer using that
implementation.  Now ANSI-C++ would override such private agreements.

|In fact, is there anything preventing a C implementation from putting
|
|    static int putchar(int c) { return putc(c, stdout); }
|
|in its <stdio.h> header?  Is there anything preventing a compiler
|from expanding a call to "putchar" inline, given this definition?
|(Urgh, I wish I had a copy of the standard around...)

Again, it is not an issue of "preventing" it is an issue of "requiring" --
the proposed ANSI-C++ changes would require changes to existing
implementations, breaking the agreements already established between
that implementation and the expectations of conforming programs for
that implementation.

|> Also, note that a conforming C implementation of these standard library
|> functions using macros can accept a superset of the parm types that a
|> C++ extern implementation or inline implementation can accept.
|
|Well, yes, but can a conforming, i.e., portable program depend on this?

You confuse conforming with strictly conforming.  A conforming program
CAN make such expectations.  A strictly-conforming program cannot.
Conforming and strictly conforming programs are BOTH "legal" programs.
Neither is a "bad" program.  They might just meet differing requirements.
For example, it is not uncommon for otherwise strictly-conforming
programs to contain conforming sections surrounded by #ifdefs for
various target implementations, such that the ultimate in performance
can be achieved on each of those implementations.  This is "good".
The programmer took such effort because such effort was needed.
Such effort should be respected.  There should not be new requirements
added in the name of "portability" that now defeats such efforts.

|My K&R says `int putchar(int c)'.  I have to assume that this is what
|I have when I write a call to `putchar'.  Even if I have a whiz-bang
|extended C compiler that allows me to pass other things to `putchar',
|I have to assume any code that uses this will break when I move to
|another compiler, be it a C++ compiler or just another vendor's C.

Yes.  Such a program would be conforming not strictly conforming.
Again, there is nothing 'wrong' with conforming programs, they are
just not very portable.  It was the expressed intent of C to allow
BOTH programs that are not portable, and programs that ARE portable,
the difference being in the effort and desires of the program writer.
Frequently portability comes at an expense of speed, for example,
or at the expense of writing [and generating] more code.

|> Thus, requiring that a macro *not* be
|> used for implementation requires that some possible conforming programs
|> will no longer be accepted.
|
|As I understand it, even if it's required for `putchar' and to `putc'
|to be macros, it is still legal for their definitions to be
|
|    #define putc(c,f) fputc(c,f)
|    #define putchar(c) putc(c,stdout)
|
|which means that a conforming program couldn't possibly pass anything
|to `putchar' which it couldn't also pass to `fputc', which is certainly
|allowed to be a function.

No.  Again you confuse conforming with strictly conforming.  What
you say above is true of strictly conforming programs, not of conforming
programs.

|What other properties of `putchar' being a macro or not could a
|conforming program depend on?
|
|> The other side of the coin is that it then becomes impossible for
|> programmers to specify *whether or not* they want an inline implementation,
|> which is a stated intent of the original ANSI-C standard library 
|> macro/function implementations.
|
|Could you please provide a quotation?

Again, section 4.1.6 of X3.159-1989

A tiny subpart says:

"Any macro definition of a function can be suppressed LOCALLY by
enclosing the name of the function in parentheses, ...."

[emphasis mine]

Clearly the intent is that the programmer can CHOOSE where and when
to use the macro implementation [fast speed] and  where and when
to use the function implementations [small size] and the programmer
can mix and match these choices as the *programmer* so desires.

Please read the whole paragraph if this remains unclear to anyone.

|> So then the next person has to somehow magically discover that the "meaning"
|> of these "standard" functions have been changed from the C++ standard have
|> been changed from the C standard....
|
|What has actually changed, that this next maintainer could perceive?

Speed verses size tradeoffs have been removed from the intended control
of both the programmer and the implementor.  Thus the next maintainer says
"Gosh darn, recompiling this under C++ made it 20% bigger and 2X as slow -- 
clearly C++ s*cks the big one.  What do I do now?"

|Okay, the performance has changed some, but performance can change even
|when you install a new rev of the "same" compiler.

But typically implementors are not REQUIRED to make their next generation
compiler generate bigger slower code.  Customers do complain, and rightly
so, when a next generation compiler does worse on their programs than
their previous compiler.  The ANSI-C++ committee would now so require.
I believe this would be a bad decision for C++.  What would happen is
that C programs would simply continue to be maintained as C programs, 
and would not be ported over to C++.  Because who wants bigger slower code?

Newsgroups: comp.lang.c++