[gnu.g++.bug] bug 12178901

rfg@ICS.UCI.EDU (12/18/89)

// bug 12178901 - linkage of global const objects

// What is the `linkage' of const objects in C++?

// GCC generates code for the following such that `ci' has
// extern linkage, and such that its initial value is 99.

// G++ 1.36.1 however optimizes this declaration into nothing.
// Thus, if some other file contained `extern const int ci;'
// that other file would fail to link.

const int ci = 99;

tiemann@LURCH.STANFORD.EDU (Michael Tiemann) (12/19/89)

   Date:     Sun, 17 Dec 89 11:36:06 PST
   From: rfg@ics.uci.edu


   // bug 12178901 - linkage of global const objects

   // What is the `linkage' of const objects in C++?

   // GCC generates code for the following such that `ci' has
   // extern linkage, and such that its initial value is 99.

   // G++ 1.36.1 however optimizes this declaration into nothing.
   // Thus, if some other file contained `extern const int ci;'
   // that other file would fail to link.

   const int ci = 99;

If you want GNU C++ to export these declarations, it is sufficient
only to declare `extern const int ci;' prior to declaring its initial
value.  This gives expected behavior in expected case, namely that the
file which provides the definition of ci will also include a files
which contains its extern declaration.

Unless you have been asleep for the past year, you know that declaring
things extern in C files (rather than in .h files) is more and more
becomming a no-no.

Michael

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/19/89)

In article <8912171136.aa08237@ICS.UCI.EDU> rfg@ICS.UCI.EDU writes:
    
    // bug 12178901 - linkage of global const objects
    
    // What is the `linkage' of const objects in C++?

storage class, not linkage: static
    
    // GCC generates code for the following such that `ci' has
    // extern linkage, and such that its initial value is 99.

Ansi C inanely mandates it.

    // G++ 1.36.1 however optimizes this declaration into nothing.
    // Thus, if some other file contained `extern const int ci;'
    // that other file would fail to link.
    
    const int ci = 99;

BS once posted a persuasive discussion of why const in C++ has default
storage class static (and thus local linkage). If you want your const to
have global linkage, use

    extern const ci = 99;

Now, now, I am disappointed :-) about rfg not reading one of my previous
fundamental and world enlightening contributions (:-> :-> :->): I posted a
while ago some guidelines for C++ coding in which I suggested that the
storage class and inline or const should always be explicitly indicated in
C++ programs.  Among the reasons I gave were the different rules concerning
inlines and consts, which vary between Ansi C and C++, between releases of
GNU C++ (which used, until 1.35?, to have global linkage by default for
consts), and between the various C++ compilers (some do have member
functions inline by default, some do not, to some inlines are static, to
some are not, etc...).

However, the official C++ 2.0 position is:

	const objects are static by default, and can be merrily optimized
	away if no address is taken to them;

	members functions defined and not just declared in the class
	definition are inline by default;

	inline functions are static by default (less sure of this, but I
	really hope so).

Another interesting note: somebody once asked how one can specify the type
of linkage for member functions, if it is to be local. Easy, at least
with GNU C++: when *defining* the function, you can use static without
it meaning that it is a class member function, e.g.:

	class first { int i; int f (int); }; // in first.H

	static int first::f(int i) { .... } // in first.C

In other words: static in a member function *declaration* is quite
different from static in a *definition*; not so inline, thank goodness.

Same goes for inlining etc... My advice, as in the posted guidelines, is to
*never* define member functions in the class definition, but always
separately, even if they are to be inline; e.g., after the previous example:

	class first { int i; int f (int); int g(int); }; // in first.H
	static inline first::g(int i) { .... } // in first.H as well

	static int first::f(int i) { .... } // in first.C
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

rfg@ics.uci.edu (Ron Guilmette) (12/20/89)

In article <1516@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>In article <8912171136.aa08237@ICS.UCI.EDU> rfg@ICS.UCI.EDU writes:
>    
>    // bug 12178901 - linkage of global const objects
>    
>    // What is the `linkage' of const objects in C++?
>
>storage class, not linkage: static
>    
>    // GCC generates code for the following such that `ci' has
>    // extern linkage, and such that its initial value is 99.
>
>Ansi C inanely mandates it.
>
>    // G++ 1.36.1 however optimizes this declaration into nothing.
>    // Thus, if some other file contained `extern const int ci;'
>    // that other file would fail to link.
>    
>    const int ci = 99;
>

>Now, now, I am disappointed :-) about rfg not reading one of my previous
>fundamental and world enlightening contributions (:-> :-> :->): ...

Sorry Peter.  I'll pay closer attention from now on. :-)

Anyway, many people have now corrected me regarding the C++ default linkage
for const objects.  Now I only wish that I understood why my postings to
gnu.g++.bug are getting automatically cross posted to comp.lang.c++.
I wish that this were not the case because it just means that my ignorance
becomes obvious to more people this way. :-)

// rfg

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/21/89)

    Anyway, many people have now corrected me regarding the C++ default linkage
    for const objects.

This is really being a language lawyer, but: linkage != storage class.  From
what I understand, const implies by default the *storage class* static; this
in turn, at file scope, implies local linkage. To see the difference,
consider const entities local to a block; is their default storage class
"auto" or "static"? To me it is "static" in C++ (and "auto" in Ansi C), even
if linkage only really makes sense at file scope level.

The question is not as inane as it looks, because from it depends whether it
is ok to use the address of a const object outside the block in which it is
declared, something that may be actually useful.

In any case, follow Peter's paternal advice: *always* specify the storage
class in C++, *especially* on inlines and consts, inside and outside classes,
functions, blocks.

Storage classes in C and C++ are a difficult issue; for example, the
Register C Compiler from AT&T, if I remember well, on the 386, will
forbid the explicit use of the "auto" storage class on parameters.
I think this is wrong...

By the way, BUG REPORT:

	with GNU C++ 1.35.0, procedure parameters may be defined with
	storage classes "static" and "extern", which is (I reckon) not
	allowed, without complaint; these are ignored. The same applies when
	the parameters are declared in a prototype; the following compiles
	without problems:

		extern "C" extern int printf(extern const char *,...);

		main(extern const int argc,static char **const argv)
		{
		    (void) printf("argc %d, argv %0lx\n",
					argc,(long unsigned) argv);
		    return 0;
		}

Ah, yes, by the way: GNU C++ at least does not allow parameters to have
storage class 'typedef', thank goodness, but this probably means that typedef
is not really handled as a storage class, because a syntax error is
signaled, which is wrong, because *syntactically* typedef could well appear
in any declaration; it is a *semantic* error though, just like for static and
extern.

Just to show off: how many of you did know that "typedef" technically is a
storage class in C (and C++), and that 'extern "C"' does *not* imply
the "extern" storage class, because it is not a storage class?

Does it matter that 'extern "C"' is not a storage class ? YES!  for example,
both of these are accepted gleefully by GNU C++ (and correctly, I believe):

	extern "C" typedef int algebraic; /* correct, but somewhat pointless */
	extern "C" static unsigned zero = 0; /* also a bit pointless */

Note that the proper way to declare an external C constant in C++
is the following:

	extern "C" extern const unsigned one; /* declaration, in .H */

	extern "C" extern const unsigned one = 1; /* definition, in .C */

If you omit the extern from the first line, it becomes a *definition* too,
and is illegal under C++ 2.0 rules, because now it is illegal to have more
than one definition of a variable (contrarily to customary C practice, but
wisely).

Notice that since mangling does not apply to variable names yet (I am sure
that eventually Stroustrup will find a way to allow overloading of variables
too, :-> -- only half a joke here, consider templates), 'extern "C"' applied
to variable names is not strictly necessary, but I suggest to use it
nonetheless, to show that the variable is actually defined and used also by
non C++ code.

In other words: C++ has even worse overloading of reserved words than C,
and we must suffer this, as the only way out would be either a proliferation
of new reserved words, or abandoning the C type structure, both of which
would make C++ very much less C compatible.

I think that Ron Guilmette (I remember he being the author) has a few new/old
interesting test cases to add to his fine C++ test suite... And a little more
work for Michael Tiemann (God knows how cfront behaves with all this).
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk