Topic: Why no classes with internal linkage?


Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 1 Jul 1993 17:06:38 GMT
Raw View
In article <rfgC9H6I0.CJo@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
> Allow me a second try:
>
>   file1.c:
>   -----------------------------------------------------------------
>   struct S {
>    int my_member;
>   };
>   -----------------------------------------------------------------
>   file2.c:
>   -----------------------------------------------------------------
>   struct S {
>    int your_member;
>   };
>   -----------------------------------------------------------------
>
> Unless there is something else I have missed, the above code is perfectly
> valid in ANSI C, and yet could be rejected by a C++ compiler on the grounds
> that it violates the "one definition rule".

You missed the linkage of these classes.  Both classes S have internal
linkage by the ARM rules, and so the one-definition rule does not
relate them.  The above code is perfectly valid in C++ as well.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Wed, 30 Jun 1993 22:04:27 GMT
Raw View
In article <rfgC9FFL2.ur@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
> In article <1993Jun24.201536.17561@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:

> >On the contrary, enforcing the one-definition rule is practical for current
> >implementations.
>
> [...] you are arguing on the basis of a hypothetical... and you
> are thus standing on quicksand.

I may be one of the first to set foot in new territory.
Time will tell whether it's terra firma -- it's not necessarily quicksand.

> >... it's a lot easier than templates.
>
> What isn't?

Let me be more clear about the relationship to templates.  What's hard in
implementing enforcement of the one-definition rule is simply that
the implementation needs a mechanism to do some analysis at the
time the program is linked, based on information gleaned during compilation.
It's hard because it must be kept convenient for users, many of whom
are accustomed to linking code together with whatever linking tools
happen to fit their needs.  Template implementations provide the
link-time mechanism.  The hard part is done.

> >In another article Ron Guilmette states his belief that in C++ there "is no
> >such thing as a type name that has external linkage."  This may be
> >true of names declared as typedefs, but I think Ron applies this to class
> >names as well, which is incorrect.  Section 3.3 of the ARM gives a rule
> >for when class names are internally linked, and says that all other class
> >names have external linkage.
>
> So what?  To the best of my knowledge, no existing implementation of C++
> actually provides *any* type with external linkage.

What would it take to convince you otherwise?  How do you think
external linkage of class names would show up as a feature that makes
a difference to users?

> In simpler terms, the current reality (notwithstanding Bjarne's desire that
> C++ should someday differ from ANSI C in this regard) is that current
> implementors (and current implementations) of C++ do *exactly* as all
> implementations of ANSI C (and pre-ANSI C) do... i.e. they NEVER give
> any type external linkage.

Bjarne didn't do this in order to create an incompatibility with C.
It helps a great deal in understanding overloaded functions.

     translation unit 1                translation unit 2
     ------------------                ------------------

     struct s { char *p; };            struct s { char *p; };
     struct c { char *p; };            struct c { char *p; };

     extern void f(struct s);          void f(struct s) {}
     void f(struct c) {}               extern void f(struct c);

     extern void g(struct c);          void g(struct s) {}

In C, the function g is fine.  The function f will get a multiple definition
error at link time.

In C++, you will usually get a link-time diagnostic if translation unit 1 has
a call to the function g.  The function f is fine.  This is easy to
understand or explain, on the basis that the names s and c, and their types,
are known between translation units (i.e. externally linked).  The
consequence of external linkage is that the declarations of f match
up with the appropriate definitions.

All C++ implementations respect the external linkage of class names in
this way.

Here's another case in which it's plain that class names are externally
linked.

     translation unit 1                translation unit 2
     ------------------                ------------------

     class A { static int i; };        class A { static int i; };
     int i = 1;                        int i = 2;

Most implementations of C++ will give a link-time diagnostic indicating
that A::i is defined more than once.

I know Ron is familiar with the behavior of the above examples.
I believe that he does not wish to force C++ implementations to
change their treatment of these, even in the case of the
function g which works in C but not in C++.  When he says that
C++ has no external linkage for type names, he may have some other
explanation of the above behavior up his sleeve.  I like the ARM's
approach.

> they may choose
> to adopt Bjarne's words in preference to all known existing practice and
> existing implementation experience,

Rather, note that the ARM calls for classes to be externally linked primarily
in situations like the examples above where it makes a difference to
existing implementations.

Moreover, if one agrees that external linkage is needed for some class
names, it follows that externally-linked class names need to satisfy
some form of structural compatibility, so that the program will
work meaningfully.  The structural compatibility requirements for
structs in the C standard are a minimal starting point.  But C++ classes
are so much more complex that a lot of work would be involved
in defining structural compatibility for them.

Often, those working on the C++ standard don't seem to realize it,
but the heart of every workable proposal for the one-definition rule
for classes is the requirement:

  (*)  Classes that are externally linked with one another shall be
       structurally equivalent.

[It may sound as if I've abandoned the name equivalence camp, but I don't
think so.  I'm trying to defend it.]

Should violations of this rule (*) result in required diagnostics, or
undefined behavior?  Ron's several citations of existing practice do not
refute my points, but they do amount
to a good reason to leave it as undefined behavior, and not add an
untested requirement that implementations diagnose these errors at link
time, even though it's feasible.

> I see no reason that such an unnecessary, unjustified, and frivolous in-
> compatability (relative to ANSI C) should be made a part of the final C++
> standard.

We see some of the need in the examples above, and I'm sure Bjarne
justified this aspect of C++ long ago.  Is this another situation where
Ron is disturbed mainly by the divergence in terminology?
If so then he is the one on quicksand.  Otherwise, please indicate the
problems that have arisen or are bound to arise as a result of these
rules that have been around for years.

> Furthermore, I have some hope that X3J16 will act responsibly
> in this instance, and that they will take proper account of the fact that
> the (total?) absence of implementation and/or usage experience with types
> which have external linkage argues strongly against incorporating this
> untested notion into the final standard.



Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 8 Jul 1993 09:18:00 GMT
Raw View
In article <1993Jun30.220427.22513@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>In article <rfgC9FFL2.ur@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
>> In article <1993Jun24.201536.17561@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>
>> So what?  To the best of my knowledge, no existing implementation of C++
>> actually provides *any* type with external linkage.
>
>What would it take to convince you otherwise?  How do you think
>external linkage of class names would show up as a feature that makes
>a difference to users?

One way would be that I would get a link-time error when linking together
the compiled versions of the following two translation units:

 file1.C:
 ------------------------------------------------------------------
 struct FOO { int foo_field; };
 ------------------------------------------------------------------

 file2.C:
 ------------------------------------------------------------------
 struct FOO { double foo_field; };
 ------------------------------------------------------------------

This is *my* interpretation of what the one-definition rule is trying to
require.

>Bjarne didn't do this in order to create an incompatibility with C.
>It helps a great deal in understanding overloaded functions.
>
>     translation unit 1                translation unit 2
>     ------------------                ------------------
>
>     struct s { char *p; };            struct s { char *p; };
>     struct c { char *p; };            struct c { char *p; };
>
>     extern void f(struct s);          void f(struct s) {}
>     void f(struct c) {}               extern void f(struct c);
>
>     extern void g(struct c);          void g(struct s) {}
>
>In C, the function g is fine.  The function f will get a multiple definition
>error at link time.
>
>In C++, you will usually get a link-time diagnostic if translation unit 1 has
>a call to the function g.

Quite so, but now you have shifted the conversation (in a rather dramatic
way) away from what we were talking about.  We were talking about the linkage
of types.  Now you are making an observation about one aspect of the linkage
of *specific* overloaded *functions*.

I think this illustrates the real problem.  Folks in the C++ world tend to
use examples like the one you gave above as illustrations of "the linkage
of types" but I don't think such examples illustrate anything of the kind.
Rather, I think that they illustrate important points about the linkage
of functions.

>Here's another case in which it's plain that class names are externally
>linked.
>
>     translation unit 1                translation unit 2
>     ------------------                ------------------
>
>     class A { static int i; };        class A { static int i; };
>     int A::i = 1;                     int A::i = 2;
>
>Most implementations of C++ will give a link-time diagnostic indicating
>that A::i is defined more than once.

(Pete, I corrected a minor pair of typos in your example.)

This example also underscores the point I made above.  Here, the point you
have illustrated relates to the linkage of variables... NOT to the linkage
of types.

This allows me to reiterate my original point, which was that types do not
have external linkage.

>I know Ron is familiar with the behavior of the above examples.

Yes.

>I believe that he does not wish to force C++ implementations to
>change their treatment of these, even in the case of the
>function g which works in C but not in C++.

Correct. C++ should continue to have "type safe" linkage for functions and
objects.

>When he says that
>C++ has no external linkage for type names, he may have some other
>explanation of the above behavior up his sleeve.

See above.

>I like the ARM's approach.

I don't.  As a matter of fact, I think it's horrible.

In the good ol' days of C, we had simple and obvious terms which had simple
and obvious meanings.  Take for example "external linkage".  You didn't need
a &*&^%$ language reference manual to figure out if something had "external
linkage" or not.  You could just do an `nm' (on any UNIX system anyway) on
the object file in question and you could *see* which names were considered
to be "linkable" by the system linker.

Now I happen to like this kind of obvious and intutive simplicity when it
comes to our use of terminology.  I see no reason to take a well entrenched
(and well specified) term like "external linkage" and bend it around to the
point where it bears little relationship to the (typical) underlying
implementation mechanism anymore.  In fact, I think the whole standard
will be a much clearer document if x3j16 *avoids* the mutilation of various
bits of well-established ANSI C terminology (e.g. "external linkage")
wherever possible... especially since a good deal of the primitive underlying
implementation mechanism (e.g. linkers) are still fundamentally the same
(and haven't really changed radivcally in about 20+ years).

Juts remember, the things that linkers deal with are names of objects and
functions.  (I believe that statement is still true of essentially all
current implementations of C++.)  So the term "external linkage" is
still something that should have relevance only for objects and functions.

Now I admit that x3j16 may wish to invent some separate (and new) term to
help in simplifying the rules with regard to the requirements for "sameness"
which may apply to certain types across various translation units, but I
hope like hell that they will do that (ultimately) rather than simply
diluting and corrupting the meaning of a perfectly good existing term
like "external linkage".

>  (*)  Classes that are externally linked with one another shall be
>       structurally equivalent.
>
>[It may sound as if I've abandoned the name equivalence camp, but I don't
>think so.  I'm trying to defend it.]

The standard can provide rules saying what happens if a multi-translation
unit program violates either some kind of name equivalence rules for types
or some kind of structural equivalence rules for types... but it DOES NOT
have to state such rules in terms of "linkage".  Other (more appropriate)
terminology could be (and should be) used.

>Should violations of this rule (*) result in required diagnostics, or
>undefined behavior?  Ron's several citations of existing practice do not
>refute my points, but they do amount
>to a good reason to leave it as undefined behavior, and not add an
>untested requirement that implementations diagnose these errors at link
>time, even though it's feasible.

I agree, and this actually underscores my point.  In the ANSI C standard
there are number of existing (and well tested, and *definitely* implementable)
rules relating to "external linkage".  I'm suggesting that those should
simply be adopted as part of the basis of how C++ works.  Separately, an
additional set of rules relating to the sameness of types across translation
units should be given for C++... in terms of something other than "linkage".
Those latter rules would (of course) need to say when diagnostics are
required for violations of *those* rules and when violations of *those*
rules result in undefined behavior.

>> I see no reason that such an unnecessary, unjustified, and frivolous in-
>> compatability (relative to ANSI C) should be made a part of the final C++
>> standard.
>
>We see some of the need in the examples above...

I disagree.

> ...and I'm sure Bjarne justified this aspect of C++ long ago.

If those justifications were based upon examples such as the ones you gave
above, then I would claim that they are totally inadequate, and that in
fact, even talking about the external linkage of types is a lot like
talking about the disk brakes on a rhinoceros.  (BTW, rhinoceri don't
have disk brakes.  Neither do types have external linkage.)

>Is this another situation where
>Ron is disturbed mainly by the divergence in terminology?

It isn't mere divergence that I'm disturbed by... it is downright destruction
of terminology which formerly had a simple, obvious, and meaningful relation-
ship not only to the way the language rules actually worked but also to
the way that actual implementations worked (under the hood).

This is a rather important point.  Languages like C and C++ are *supposed*
to be defined in such a way that they are really well and obviously connected
to the underlying implementation mechanisms.  That's why ANSI C intentionally
left certain things in certain categories... like for example the semantics
on integer overflow were left in the "undefined behavior" category because
various underlying hunks of CPU hardware handled such things differently.

>From what I've seen of the standards committee's efforts, it's more likely
>that the one-definition rule will be lost, due to the lack of sufficient
>interest in the difficult task of extending structural compatibility to
>C++ classes.

Well, I certainly *do* hope that the final standard will say *something*
about what happens if you try to do nasty thing like those illustrated
by your examples above.  I *do* hope that it is required that *all*
implementations are required to do the same things for such examples.

Even ANSI C says that you cannot put two definitions of the same single
function or variable into the same single (linked) program.

(Types are a different issue entirely however.)

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 8 Jul 1993 09:22:33 GMT
Raw View
In article <1993Jul1.170638.23124@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>In article <rfgC9H6I0.CJo@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
>> Allow me a second try:
>>
>>   file1.c:
>>   -----------------------------------------------------------------
>>   struct S {
>>    int my_member;
>>   };
>>   -----------------------------------------------------------------
>>   file2.c:
>>   -----------------------------------------------------------------
>>   struct S {
>>    int your_member;
>>   };
>>   -----------------------------------------------------------------
>>
>> Unless there is something else I have missed, the above code is perfectly
>> valid in ANSI C, and yet could be rejected by a C++ compiler on the grounds
>> that it violates the "one definition rule".
>
>You missed the linkage of these classes.  Both classes S have internal
>linkage by the ARM rules, and so the one-definition rule does not
>relate them.  The above code is perfectly valid in C++ as well.

My thanks to both John Spicer and Scott Turner for correcting my example
yet again.

Let me try one last time to get this right.

   file1.c:
   -----------------------------------------------------------------
   struct S {
    int my_member;
   };

 struct S object1;
   -----------------------------------------------------------------
   file2.c:
   -----------------------------------------------------------------
   struct S {
    int your_member;
   };
 struct S object2;
   -----------------------------------------------------------------

OK.  Have I got it right now?

Is it indeed the case that if the above two translation units were linked
together into a single program that the current x3j16 working paper would
(at the very least) permit a C++ implementation to reject the program at
link time?

If so, then this illustrates the ANSI C incompatability I have been driving
at.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 1 Jul 1993 07:27:02 GMT
Raw View
In article <1993Jul1.002430.20351@borland.com> pete@borland.com (Pete Becker) writes:
+In article <rfgC9G2oL.LCB@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
+>>
+>> I don't agree that the one definiton rule is either odd or novel.
+>
+>In the case of types (and for a old C programmer) it is both.
+>
+>>What's peculiar about it is that the language definition says something about
+>>it. In C the rule is almost as important, but it's never made explicit. If you
+>>have a global function that takes a pointer to a struct S you better not have
+>>two definitions of S and try to use the same function on those two different
+>>structs.
+>
+>I beg to differ.  I believe that the following usage is (in C) highly
+>portable, and that it will produce the expected results every time, even
+>though the two structure types are "different".
+>
+> file1.c:
+> -----------------------------------------------------------------
+> struct S {
+>  int my_member;
+> };
+>
+> struct S object1;
+>
+> extern void operate_on_S (struct S);
+>
+> void caller1 () { operate_on_S (object1); }
+> -----------------------------------------------------------------
+> file2.c:
+> -----------------------------------------------------------------
+> struct S {
+>  int your_member;
+> };
+>
+> struct S object2;
+>
+> void operate_on_S (struct S arg) { .... arg.your_member ... }
+>
+> void caller2 () { operate_on_S (object2); }
+> -----------------------------------------------------------------
+>
+
+ I suppose it depends on exactly how you define "different".

That is a very good observation Pete.  It does indeed depend on how one
defines the term "different".

At this point, I would like to say that (for the purposes of the point I
was trying to make) I define "different" the same way as it is defined
for the sake of the C++ "one definition rule", but to tell the truth,
I don't even know if the terms "same" and "different" have even been
defined when it comes to C++ class types!  In particular, are the
following types considered "same" or "different" as far as the C++
"one definition rule" is concerned?

 struct S { int my_member; };
 struct S { int your_member; };

I don't know.  My (naive?) assumption is/was that these types are considered
to be "different" types in C++.  If indeed they are, then my earlier statements
are correct, and, as illustrated by my example above, the C++ "one definition
rule" creates a serious (and unnecessary) incompatability with ANSI C.

Having said all that however, let me further note that it it is probably
premature for me to even be making such a claim, since at this point in time
the specification of the C++ language is still so incomplete that I doubt
that either the ARM or the current X3J16 working paper even contain any
statements which would make it crystal clear how one could know whether
two types are the same type or different types.

Translation"  All arguments relating to the C++ "one definition rule" are
based on quicksand until such time as we have rules which tell us when we
have one definition or a thing, and when we have two... but even these
fundamental rules haven't been written down yet.

P.S.  Note that the ANSI C standard *does* contain very clear rules which
tell you how to determine whether or not two structure types are "compatible".
See the ANSI C standard, section 3.1.2.6, first paragraph.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 1 Jul 1993 07:37:12 GMT
Raw View
In article <25898@alice.att.com> ark@alice.UUCP () writes:
+In article <rfgC9G2oL.LCB@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
+
+> I beg to differ.  I believe that the following usage is (in C) highly
+> portable, and that it will produce the expected results every time, even
+> though the two structure types are "different".
+
+>  file1.c:
+>  -----------------------------------------------------------------
+>  struct S {
+>   int my_member;
+>  };
+
+>  struct S object1;
+
+>  extern void operate_on_S (struct S);
+
+>  void caller1 () { operate_on_S (object1); }
+>  -----------------------------------------------------------------
+>  file2.c:
+>  -----------------------------------------------------------------
+>  struct S {
+>   int your_member;
+>  };
+
+>  struct S object2;
+
+>  void operate_on_S (struct S arg) { .... arg.your_member ... }
+
+>  void caller2 () { operate_on_S (object2); }
+
+Portable or not, it's non-conforming.
+
+ANSI C standard 3.1.2.6:
+
+ ... two structure, union, or enumeration types declared in
+ separate translation units are compatible if they have the
+ same number of members, the same member names, and compatible
+    ^^^ ^^^^ ^^^^^^ ^^^^^

Quite true.  I stand corrected.  The above code is clearly portable, but
is nontheless non-conforming (to the ANSI C standard).  Allow me a second try:

  file1.c:
  -----------------------------------------------------------------
  struct S {
   int my_member;
  };
  -----------------------------------------------------------------
  file2.c:
  -----------------------------------------------------------------
  struct S {
   int your_member;
  };
  -----------------------------------------------------------------

Unless there is something else I have missed, the above code is perfectly
valid in ANSI C, and yet could be rejected by a C++ compiler on the grounds
that it violates the "one definition rule".

I see no compelling reason for such an incompatability between the two
languages.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: jhs@edg.com (John H. Spicer)
Date: Thu, 1 Jul 1993 12:42:58 GMT
Raw View
In article <rfgC9H6I0.CJo@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>
>Quite true.  I stand corrected.  The above code is clearly portable, but
>is nontheless non-conforming (to the ANSI C standard).  Allow me a second try:
>
>  file1.c:
>  -----------------------------------------------------------------
>  struct S {
>   int my_member;
>  };
>  -----------------------------------------------------------------
>  file2.c:
>  -----------------------------------------------------------------
>  struct S {
>   int your_member;
>  };
>  -----------------------------------------------------------------
>
>Unless there is something else I have missed, the above code is perfectly
>valid in ANSI C, and yet could be rejected by a C++ compiler on the grounds
>that it violates the "one definition rule".
>
>I see no compelling reason for such an incompatability between the two
>languages.
>
>--
>
>-- Ronald F. Guilmette ------------------------------------------------------
>------ domain address: rfg@netcom.com ---------------------------------------
>------ uucp address: ...!uunet!netcom.com!rfg -------------------------------


Actually, there are internally linked classes and your example makes use of
them.

The WP (3.3) says:

 [A name ... has internal linkage.]  So does the name of a class that
 has not been used in the declaration of an object, function, or
 class that has externa linkage and has no static members, and no
 noninline functions.

The first part is just to provide context for what "So does" means.

John Spicer





Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 24 Jun 1993 20:15:36 GMT
Raw View
In article <rfgC91Js8.206@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
> Thanks again Peter for noting the fact that the C++ notion of a "one definition
> rule" for types is at best ill defined.
>
> The truth of the matter is that this odd (and novel) sort of notion, intro-
> duced by C++, is both ill specified and essentially unenforcable by current
> implementation techniques (and current implementations).

On the contrary, enforcing the one-definition rule is practical for current
implementations.  I agree the rule is ill-specified.  There are lots of little
unresolved nits, like "Is it OK to declare
 struct S { int i; };
in one translation unit, and
 struct S { signed int i; };
in another?"  But despite the lack of progress in resolving these nits,
there is a great opportunity for an implementation to come along that
diagnoses violations of the one-definition rule with direct and clear
messages.  For example, if a third translation unit says
 struct S { long i; };
then there's no doubt that any version of the one-definition rule would
call this an error.  An implementation can detect this at the time the
translation units are linked together -- it's a lot easier than templates.

Note that the one-definition rule has been a part of C++ since the first
edition of "The C++ Programming Language" by Stroustrup.

------

In another article Ron Guilmette states his belief that in C++ there "is no
such thing as a type name that has external linkage."  This may be
true of names declared as typedefs, but I think Ron applies this to class
names as well, which is incorrect.  Section 3.3 of the ARM gives a rule
for when class names are internally linked, and says that all other class
names have external linkage.

------

In another article Brad Daniels qualifies his appeal for class names having
internal linkage, saying he didn't mean precisely that, but that he wanted
the internal linkage to affect member definitions.  Please, there's nothing
inherently goofy about the notion of externally-linked class names, or
internally-linked class names.  In fact, it's clearer than trying to say
that a linkage declaration for the class actually affects its static and
function members.

Brad's suggestions appeal to me, because the rule for internally linked classes
in 3.3 of the ARM is plainly a heuristic.  It works most of the time, and
when it doesn't work it may be problematic to straighten one's program out.
Anyone who runs into that kind of situation would benefit from an extension
which allowed class name linkage to be specified explicitly.

Am I recommending an extension?  No, just discussing one. :-)
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Wed, 30 Jun 1993 08:58:13 GMT
Raw View
In article <1993Jun24.201536.17561@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>In article <rfgC91Js8.206@netcom.com>, rfg@netcom.com (Ronald F. Guilmette) writes:
>> Thanks again Peter for noting the fact that the C++ notion of a "one definition
>> rule" for types is at best ill defined.
>>
>> The truth of the matter is that this odd (and novel) sort of notion, intro-
>> duced by C++, is both ill specified and essentially unenforcable by current
>> implementation techniques (and current implementations).
>
>On the contrary, enforcing the one-definition rule is practical for current
>implementations.

I will believe that when someone implements it and when someone else uses that
implementation.

Until then Scott, you are arguing on the basis of a hypothetical... and you
are thus standing on quicksand.

> I agree the rule is ill-specified.

Good.  We agree about that at least.

>... it's a lot easier than templates.

What isn't?

>Note that the one-definition rule has been a part of C++ since the first
>edition of "The C++ Programming Language" by Stroustrup.

Ok.  So tell me this.  If an ARM falls in the forrest, and there is no
one there to implement it, does it make any practical difference?

>In another article Ron Guilmette states his belief that in C++ there "is no
>such thing as a type name that has external linkage."  This may be
>true of names declared as typedefs, but I think Ron applies this to class
>names as well, which is incorrect.  Section 3.3 of the ARM gives a rule
>for when class names are internally linked, and says that all other class
>names have external linkage.

So what?  To the best of my knowledge, no existing implementation of C++
actually provides *any* type with external linkage.

In simpler terms, the current reality (notwithstanding Bjarne's desire that
C++ should someday differ from ANSI C in this regard) is that current
implementors (and current implementations) of C++ do *exactly* as all
implementations of ANSI C (and pre-ANSI C) do... i.e. they NEVER give
any type external linkage.

That is the current objective reality.  All else is fantasy and wishful
thinking.

Now I freely admit that the ANSI/ISO C++ standardization committee may
(as they have done so many times in the past) totally ignore the objective
and immediate reality of all existing implementations, and they may choose
to adopt Bjarne's words in preference to all known existing practice and
existing implementation experience, but if they do so in this particular
case (by endorsing the idea that types may have external linkage) then
they will be making a DOUBLY BIG MISTAKE in my opinion.  In the first
place they will be mandating something for which there is neither
implementation nor usage experience.  In the second place (and just as
significantly) they will be endorsing a radical departure from the well
specified, well established, and well tested semantics already documented
for types in the ANSI C standard.  This would create yet another direct
incompatability between the C and C++ languages and would create yet
more difficulties (on top of the numerous ones already cataloged in the
current X3J16 drafts) for programmers trying to simply recompile existing
ANSI C programs as C++ programs.

I see no reason that such an unnecessary, unjustified, and frivolous in-
compatability (relative to ANSI C) should be made a part of the final C++
standard.  Furthermore, I have some hope that X3J16 will act responsibly
in this instance, and that they will take proper account of the fact that
the (total?) absence of implementation and/or usage experience with types
which have external linkage argues strongly against incorporating this
untested notion into the final standard.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Wed, 30 Jun 1993 17:17:09 GMT
Raw View
In article <1993Jun22.220025.27617@borland.com> pete@borland.com (Pete Becker) writes:
>In article <rfgC91Js8.206@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>>Thanks again Peter for noting the fact that the C++ notion of a "one definition
>>rule" for types is at best ill defined.
>>
>>The truth of the matter is that this odd (and novel) sort of notion, intro-
>>duced by C++, is both ill specified and essentially unenforcable by current
>>implementation techniques (and current implementations).
>>
>
> I don't agree that the one definiton rule is either odd or novel.

In the case of types (and for a old C programmer) it is both.

>What's peculiar about it is that the language definition says something about
>it. In C the rule is almost as important, but it's never made explicit. If you
>have a global function that takes a pointer to a struct S you better not have
>two definitions of S and try to use the same function on those two different
>structs.

I beg to differ.  I believe that the following usage is (in C) highly
portable, and that it will produce the expected results every time, even
though the two structure types are "different".

 file1.c:
 -----------------------------------------------------------------
 struct S {
  int my_member;
 };

 struct S object1;

 extern void operate_on_S (struct S);

 void caller1 () { operate_on_S (object1); }
 -----------------------------------------------------------------
 file2.c:
 -----------------------------------------------------------------
 struct S {
  int your_member;
 };

 struct S object2;

 void operate_on_S (struct S arg) { .... arg.your_member ... }

 void caller2 () { operate_on_S (object2); }
 -----------------------------------------------------------------

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Wed, 30 Jun 1993 17:20:21 GMT
Raw View
In article <1993Jun22.220025.27617@borland.com> pete@borland.com (Pete Becker) writes:
>In article <rfgC91Js8.206@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>>Thanks again Peter for noting the fact that the C++ notion of a "one definition
>>rule" for types is at best ill defined.
...
> The change that C++ makes from C is typesafe linkage, which pretty
>much necessitates the prohibition on conflicting definitions of the same type
>unless you also prohibit global functions that take parameters of that type.

I claim that this statement is pretty much false.

What makes you think that the C++ notion of typesafe linkage (for objects
and functions) necessitate anything with respect to the linkage of types?

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: pete@borland.com (Pete Becker)
Date: Thu, 1 Jul 1993 00:24:30 GMT
Raw View
In article <rfgC9G2oL.LCB@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>> I don't agree that the one definiton rule is either odd or novel.
>
>In the case of types (and for a old C programmer) it is both.
>
>>What's peculiar about it is that the language definition says something about
>>it. In C the rule is almost as important, but it's never made explicit. If you
>>have a global function that takes a pointer to a struct S you better not have
>>two definitions of S and try to use the same function on those two different
>>structs.
>
>I beg to differ.  I believe that the following usage is (in C) highly
>portable, and that it will produce the expected results every time, even
>though the two structure types are "different".
>
> file1.c:
> -----------------------------------------------------------------
> struct S {
>  int my_member;
> };
>
> struct S object1;
>
> extern void operate_on_S (struct S);
>
> void caller1 () { operate_on_S (object1); }
> -----------------------------------------------------------------
> file2.c:
> -----------------------------------------------------------------
> struct S {
>  int your_member;
> };
>
> struct S object2;
>
> void operate_on_S (struct S arg) { .... arg.your_member ... }
>
> void caller2 () { operate_on_S (object2); }
> -----------------------------------------------------------------
>

 I suppose it depends on exactly how you define "different". From the
perpsective of the programmer who wrote the function operate_on_S these two
versions of S are the same. Definint S "different"ly can lead to problems:

 struct S
 {
 char name[17];
 };

 operate_on_S almost certainly won't do what it's expected to do when
called with this form of S.
 -- Pete




Author: ark@alice.att.com (Andrew Koenig)
Date: 30 Jun 93 20:37:48 GMT
Raw View
In article <rfgC9G2oL.LCB@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:

> I beg to differ.  I believe that the following usage is (in C) highly
> portable, and that it will produce the expected results every time, even
> though the two structure types are "different".

>  file1.c:
>  -----------------------------------------------------------------
>  struct S {
>   int my_member;
>  };

>  struct S object1;

>  extern void operate_on_S (struct S);

>  void caller1 () { operate_on_S (object1); }
>  -----------------------------------------------------------------
>  file2.c:
>  -----------------------------------------------------------------
>  struct S {
>   int your_member;
>  };

>  struct S object2;

>  void operate_on_S (struct S arg) { .... arg.your_member ... }

>  void caller2 () { operate_on_S (object2); }

Portable or not, it's non-conforming.

ANSI C standard 3.1.2.6:

 ... two structure, union, or enumeration types declared in
 separate translation units are compatible if they have the
 same number of members, the same member names, and compatible
    ^^^ ^^^^ ^^^^^^ ^^^^^
 member types; for two structures, the members shall be in the
 same order; for two structures or unions, the bit-fields shall
 have the same widths; for two enumerations, the members shall
 have the same values.

3.5.4.3

 For two function types to be compatible, both shall specify
 compatible return types.  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.       ^^^^^^^^^^^^^ ^^^^^^^^^^
 ^^^^^ ^^^^ ^^^^^^^^^^ ^^^^^

C implementations are therefore entitled to reject your example
on the basis that the declaration and definition of operate_on_S
specify incompatible types.
--
    --Andrew Koenig
      ark@research.att.com




Author: daniels@NeoSoft.com (Brad Daniels)
Date: Fri, 18 Jun 1993 21:08:29 GMT
Raw View
In article <1vq3cb$qbp@fnnews.fnal.gov> b91926@fnclub.fnal.gov (David Sachs) writes:
>In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, I wrote:
...
>|> In such cases, a class with file scope and internal linkage is ideal,
>|> but there is no way to declare such a thing.  The most logical syntax
>|> for such a declaration would be something along the lines of "class static
>|> classname ..."  While this syntax is not exceptionally pretty, it is no
>|> worse than much of what we already have, is unambiguous, and provides
>|> useful added functionality.  It seems like a natural thing to do.  Is
>|> there in fact a reason no syntax like this is supported?
>
>Why don't you submit a formal proposal to the standards committee? I would
>like to see such a feature, but there are obvious problems:

I would if I knew how.  Can it be done electronically, or do I have to use
snail mail?

>What happens if a static class overrides virtual functions of an inherited
>non-static class?

I don't see a problem here.  This case is analogous to a function returning
a pointer to a static function.  It should work in the same way.

>What happens if a non-static class defines a static class as a base.

Again, I see no problem with allowing this, except that it would be fairly
useless.  Anyplace where the non-static class is used, you would need a
declaration of the static to be in scope anywhere that the non-static class
is declared, since you can't inherit from an incomplete type, and a
static class identifier would be considered different from an identical
non-static identifier.  As is currently the case, the behavior would be
undefined if the definition of the class were different in different modules,
e.g. because the definition of the static class varies.

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: daniels@NeoSoft.com (Brad Daniels)
Date: Sat, 19 Jun 1993 21:13:30 GMT
Raw View
In article <C8sv1I.FM4@world.std.com> tob@world.std.com (Tom O Breton) writes:
>>In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, daniels@NeoSoft.com (Brad Daniels) writes:
>>|> Section 3.3 of the working document makes explicit mention of the fact
>>|> that it is not possible to declare a class name static.  What is the
>>|> rationale behind this restriction?  Clearly, people were aware of it,
>>|> since it is explicitly mentioned, but its lack seems something of a
>>|> hole in the language.  At present, the only way to declare a class which
>>|> is visible only in the implementation of another class is to declare
>>|> the "hidden" class as a nested class in the declaration of the class
>>|> using it.  This approach causes programmers some annoying scoping problems,
>>|> and also reduces the amount of information hiding available.  In many cases,
>>|> it is neither necessary nor desirable to define or declare these internal
>>|> classes in a publicly exported header file.
>>|>
>>|> In such cases, a class with file scope and internal linkage is ideal,
>>|> but there is no way to declare such a thing.  The most logical syntax
>
>Am I misunderstanding your proposal?
>
>It appears to me that what you want can be accomplished with not
>effort by simply *not* putting the class definition in a shared header
>file, as is usually done.

Yes and no.  You can regain some of the information hiding by putting
only a forward declaration of the internal class in the nested scope of
the class, then defining the class in the implementation of the class,
but you are still "publishing" the fact that you are using such a class,
and must change the header file if you wish to add additional classes
whose existence does not matter to your class's users.

Classes with internal linkage and file scope, or "static" classes, would
be much like static functions or variables.  They would be completely in-
visible outside of their individual compilation units.  At present, with
most compilers, a definition of a class with no member functions or statis
data members at file scope in an individual compilation unit is already
invisible outside of that compilation unit, since it defines no functions or
variables which require external linkage.  My proposal would simply extend
that capability to include full-blown classes.  It doesn't make sense that
one can declare static variables and functions, but not static classes.

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: pat@frumious.uucp (Patrick Smith)
Date: Sat, 19 Jun 1993 16:00:24 GMT
Raw View
tob@world.std.com (Tom O Breton) writes:
|>In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, daniels@NeoSoft.com (Brad Daniels) writes:
|>|> In such cases, a class with file scope and internal linkage is ideal,
|>|> but there is no way to declare such a thing.  The most logical syntax
|
|Am I misunderstanding your proposal?
|
|It appears to me that what you want can be accomplished with not
|effort by simply *not* putting the class definition in a shared header
|file, as is usually done.
|
|Is there something that would leave out?

This should work -- unless the name you give to the class happens
to be the same as the name of another class somewhere else in
the program.  According to the language rules, if two classes
have the same name, they have to be the same class (this is
the "one definition" rule).

In practice, if the classes don't have member functions with the
same name (or static data members), it's likely to work.  But
otherwise, it might not:

   // In foo.c:

   class MyPrivateClass {
   public:
      MyPrivateClass();
   }
   MyPrivateClass() { /* do something */ };

   // In bar.c:

   class MyPrivateClass {
   public:
      MyPrivateClass();
   }
   MyPrivateClass() { /* do something else */ };

At some point, you're likely to get a complaint about the two
definitions of the default constructor.

--
Patrick Smith
pat@tesuji.qc.ca
uunet.ca!frumious!pat




Author: oren@pashosh.weizmann.ac.il (Ben-Kiki Oren)
Date: Mon, 21 Jun 1993 06:05:17 GMT
Raw View
In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, daniels@NeoSoft.com (Brad Daniels) writes:
|> Section 3.3 of the working document makes explicit mention of the fact
|> that it is not possible to declare a class name static.  What is the
|> rationale behind this restriction?  Clearly, people were aware of it,
|> since it is explicitly mentioned, but its lack seems something of a
|> hole in the language.  At present, the only way to declare a class which
|> is visible only in the implementation of another class is to declare
|> the "hidden" class as a nested class in the declaration of the class
|> using it.  This approach causes programmers some annoying scoping problems,
|> and also reduces the amount of information hiding available.  In many cases,
|> it is neither necessary nor desirable to define or declare these internal
|> classes in a publicly exported header file.
|>
|> In such cases, a class with file scope and internal linkage is ideal,
|> but there is no way to declare such a thing.  The most logical syntax
|> for such a declaration would be something along the lines of "class static
|> classname ..."  While this syntax is not exceptionally pretty, it is no
|> worse than much of what we already have, is unambiguous, and provides
|> useful added functionality.  It seems like a natural thing to do.  Is
|> there in fact a reason no syntax like this is supported?
|>
|> Thanks,
|> - Brad
|> --
|> Brad Daniels  ` |  "If money can't buy happiness,
|> daniels@neosoft.com  |   I guess I'll have to rent it."
|> I don't work for NeoSoft, and |  - Weird Al Yenkovic
|> don't speak for my employer. |

I would think the the namespace extension provides a better mechanism for
scoping then the use of the 'static' keyword. IMHO, once `namesspace' becomes
widespread, the 'static' keyword should be used only for static variables
within a function, or static members of a class. The scoping should be
done through namespaces.

Therefore I don't see a real need to provide such an extension (and I would
bet that the committee will also think so - remember the 'inherited'
proposal? :-) On the other hand, there is no chance that the scoping semantics
of 'static' will be removed from the language, either.
Patch on patch on patch ... :-(

       Oren.

--
Life is tough and the hard ships are many.





Author: daniels@NeoSoft.com (Brad Daniels)
Date: Mon, 21 Jun 1993 13:21:53 GMT
Raw View
In article <1993Jun21.060517.12121@wisipc.weizmann.ac.il> oren@pashosh.weizmann.ac.il (Ben-Kiki Oren) writes:
>In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, I wrote:
...
>|> In such cases, a class with file scope and internal linkage is ideal,
>|> but there is no way to declare such a thing.  The most logical syntax
>|> for such a declaration would be something along the lines of "class static
>|> classname ..."  While this syntax is not exceptionally pretty, it is no
>|> worse than much of what we already have, is unambiguous, and provides
>|> useful added functionality.  It seems like a natural thing to do.  Is
>|> there in fact a reason no syntax like this is supported?
>
>I would think the the namespace extension provides a better mechanism for
>scoping then the use of the 'static' keyword. IMHO, once `namesspace' becomes
>widespread, the 'static' keyword should be used only for static variables
>within a function, or static members of a class. The scoping should be
>done through namespaces.

As far as "scoping" is concerned, you are more-or-less correct.  Nested
classes provide the ability to make classes inaccessible to and non-conflicting
with the rest of the program.  However, although these classes are private,
they are nonetheless "published".  By this I mean that I must declare them
in some form in the declaration of their parent class.  I haven't performed
an exhaustive search of the working document or ARM in order to be certain,
but I don't believe there's any way to simply put a forward declaration of a
nested class in a class declaration, then complete the declaration in the
file which defines the outer-scope class.  At least, my compiler gives me
a warning that such an internal class "can't be completed."

Anyway, the upshot of all this is that an internal implementation change
must result in unnecessary changes to the header file defining the outer
class, even when the change is completely irrelevant to the calss's users,
who don't need to know these other classes even exist.  This is a great big
nasty break in information hiding.  I should be able to make localized
decisions to use any class I need without worrying about whether I'm going
to force the rest of the world to recompile needlessly or about conflicting
with someone else's classes.  I can already do this with functions and
variables if I need to.  I can even get away with it with structs which
define no member functions or static data members.  In fact, if I had to
choose between file scope static functions and static classes, I'd choose
static classes, since you can use static classes to implement static
functions (e.g. by defining a static member function of a static class.)

Static classes would actually improve the ability to localize scope, and
also serve to provide a greater degree of true information hiding without
even asking compiler writers to go to extraordinary lengths.  The compilation
unit should be a first-class scoping entity, with full ability to localize
information as well as to make it globally visible.

>Therefore I don't see a real need to provide such an extension (and I would
>bet that the committee will also think so - remember the 'inherited'
>proposal? :-) On the other hand, there is no chance that the scoping semantics
>of 'static' will be removed from the language, either.
>Patch on patch on patch ... :-(

Given the feedback I've been getting so far, I fear you may be right.  I
feel that I have given good and substantive reasons why such a feature is
desirable given the current nature and goals of the language, but many
people seem to be focussed on trying to find alternatives to the whole
header file/implementation file concept to the extent that they are unwilling
to advocate changes which will make that concept work well.  In fact, some
seem eager to *break* the current mechanism (witness your "frowny-face"
when mentioning that a venerable feature like "static" won't be *removed*
from the language!)  While I can certainly sympathize with your desire to
break from the past and make C++ a full modern object-oriented development
environment, the language is unlikely to become such for some time.  I
want to see it get as good as possible, though, while still retaining the
features which make it work well with C in a conventional UNIX environment.
C++ is on its way to essentially requiring a complex Ada-like environment,
and I do not necessarily view this as a bad thing, but C++ cannot afford
a complete break with existing practice.  I'm developing real world
applications with C++, and I have no desire to see C++ go off into the
wild blue yonder without at least leaving me a way to talk to my operating
system and standard libraries.  I'd also prefer not to see my code break
because a particular feature offends somebody's aesthetic sensibilities.

Honestly, though, I can't envision a development environment which doesn't
have some kind of compilation unit.  If the compilation unit came to have
and implicit class scope associated with it, then no special syntax would
be needed to define classes, variables, or functions with internal linkage,
and "static" used for such could indeed go away (though it would still be
needed for static class members.)  I would rather see global identifiers
with external linkage of all kinds go away, though, than see internal linkage
go away.  Since internal linkage is here to stay, and is a good thing,
I'd like to see it fully functional.

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Tue, 22 Jun 1993 04:10:29 GMT
Raw View
In article <C8qDu4.6Jw@sugar.NeoSoft.COM> daniels@NeoSoft.com (Brad Daniels) writes:
>Section 3.3 of the working document makes explicit mention of the fact
>that it is not possible to declare a class name static.  What is the
>rationale behind this restriction?

Good question.  This is just another C (in-)compatability wart in my
opinion... and an almost totally pointless one.

>In such cases, a class with file scope and internal linkage is ideal...

I think that you may have misunderstood something somewhere along the line.

As far as I know, type names (i.e. typedef names, and tag names of struct,
union, enum, and class type) *always* have internal linkage.

What made you think otherwise?

Only things like file-scope variables and functions may have external
linkage.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: pete@borland.com (Pete Becker)
Date: Tue, 22 Jun 1993 17:49:35 GMT
Raw View
In article <rfgC908xI.AMv@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>
>As far as I know, type names (i.e. typedef names, and tag names of struct,
>union, enum, and class type) *always* have internal linkage.
>
>Only things like file-scope variables and functions may have external
>linkage.
>

 Close. Section 3.1.2.2 of the ANSI C standard:

 An identifier declared in different scopes or in the same scope more
than once can be made to refer to the same _object_ or _function_ by a
process called linkage. There are three kinds of linkage: external, internal,
and none. [emphasis added]

 That is, "linkage" only applies to objects and functions. It does
not apply to type names. There is no such thing as a static type name.
 The analog to linkage that applies to type names is the one definiton
rule. Type names themselves are not visible outside of any translation units
in which they are used. The one definition rule says that if you use the same
name to identify types in two or more separate translation units that name
must refer to the same type (whatever that means...).
 -- Pete





Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Tue, 22 Jun 1993 21:02:31 GMT
Raw View
In article <1993Jun22.174935.19492@borland.com> pete@borland.com (Pete Becker) writes:
>
> Close. Section 3.1.2.2 of the ANSI C standard:
>
> An identifier declared in different scopes or in the same scope more
>than once can be made to refer to the same _object_ or _function_ by a
>process called linkage. There are three kinds of linkage: external, internal,
>and none. [emphasis added]

Thank you Peter for providing the exact quote which confirms the point I
had made.

> That is, "linkage" only applies to objects and functions. It does
>not apply to type names. There is no such thing as a static type name.
> The analog to linkage that applies to type names is the one definiton
>rule. Type names themselves are not visible outside of any translation units
>in which they are used. The one definition rule says that if you use the same
>name to identify types in two or more separate translation units that name
>must refer to the same type (whatever that means...).
                              ^^^^^^^^^^^^^^^^^^^^^^

Thanks again Peter for noting the fact that the C++ notion of a "one definition
rule" for types is at best ill defined.

The truth of the matter is that this odd (and novel) sort of notion, intro-
duced by C++, is both ill specified and essentially unenforcable by current
implementation techniques (and current implementations).

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------




Author: daniels@NeoSoft.com (Brad Daniels)
Date: Tue, 22 Jun 1993 13:37:13 GMT
Raw View
In article <rfgC908xI.AMv@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>In article <C8qDu4.6Jw@sugar.NeoSoft.COM> daniels@NeoSoft.com (Brad Daniels) writes:
>>Section 3.3 of the working document makes explicit mention of the fact
>>that it is not possible to declare a class name static.  What is the
>>rationale behind this restriction?
>
>Good question.  This is just another C (in-)compatability wart in my
>opinion... and an almost totally pointless one.
>
>>In such cases, a class with file scope and internal linkage is ideal...
>
>I think that you may have misunderstood something somewhere along the line.
>
>As far as I know, type names (i.e. typedef names, and tag names of struct,
>union, enum, and class type) *always* have internal linkage.
>
>What made you think otherwise?
>
>Only things like file-scope variables and functions may have external
>linkage.

Member function and static data member definitions also have external linkage.
Thus, one can encounter problems at link time with multiple definitions of
locally defined classes of the same name in multiple compilation units.  The
most likely conflict is on the ctor, but given that two separate implementors
might have chosen the same name for a class, there is a high probability the
two classes will have similar member function names.  At the very least, the
ctor might get you into trouble.  You can *probably* avoid trouble by defin-
ing all the member functions associated with the class as inline functions,
which means you can get almost the full effect of classes with internal
linkage except for the case of static data members.

Regardless, you're never completely safe, and the things you can do to make
a localized class closer to safe all seem pretty kludgey.  Being able to
declare the class static would provide a concise, clear, safe way to ensure
that classes used only in the implementation of another class (and not
included as nested classes in the class declaration) would not conflict with
any other classes used in the program.

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: pete@borland.com (Pete Becker)
Date: Tue, 22 Jun 1993 22:00:25 GMT
Raw View
In article <rfgC91Js8.206@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>
>Thanks again Peter for noting the fact that the C++ notion of a "one definition
>rule" for types is at best ill defined.
>
>The truth of the matter is that this odd (and novel) sort of notion, intro-
>duced by C++, is both ill specified and essentially unenforcable by current
>implementation techniques (and current implementations).
>

 I don't agree that the one definiton rule is either odd or novel.
What's peculiar about it is that the language definition says something about
it. In C the rule is almost as important, but it's never made explicit. If you
have a global function that takes a pointer to a struct S you better not have
two definitions of S and try to use the same function on those two different
structs.
 The change that C++ makes from C is typesafe linkage, which pretty
much necessitates the prohibition on conflicting definitions of the same type
unless you also prohibit global functions that take parameters of that type.
 -- Pete





Author: daniels@NeoSoft.com (Brad Daniels)
Date: Tue, 22 Jun 1993 20:34:49 GMT
Raw View
In article <1993Jun22.174935.19492@borland.com> pete@borland.com (Pete Becker) writes:
>In article <rfgC908xI.AMv@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>>As far as I know, type names (i.e. typedef names, and tag names of struct,
>>union, enum, and class type) *always* have internal linkage.
>>
>>Only things like file-scope variables and functions may have external
>>linkage.
>>
> Close. Section 3.1.2.2 of the ANSI C standard:
>
> An identifier declared in different scopes or in the same scope more
>than once can be made to refer to the same _object_ or _function_ by a
>process called linkage. There are three kinds of linkage: external, internal,
>and none. [emphasis added]
>
> That is, "linkage" only applies to objects and functions. It does
>not apply to type names. There is no such thing as a static type name.

This is true in C and in C++.  However, a C++ class is more than just a type
definition.  It defines member functions and possibly static data members,
the first of which are "functions", and the second of which are "objects".
It is these pieces which should be able to have internal linkage, and it
would make little sense to make the distinction as to whether these functions
and objects have internal or external linkage anywhere other than the class
level.  Inline member function *could* be considered to have internal linkage,
but I think "none" describes their linkage more accurately.

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: daniels@NeoSoft.com (Brad Daniels)
Date: 16 Jun 93 20:20:26 GMT
Raw View
Section 3.3 of the working document makes explicit mention of the fact
that it is not possible to declare a class name static.  What is the
rationale behind this restriction?  Clearly, people were aware of it,
since it is explicitly mentioned, but its lack seems something of a
hole in the language.  At present, the only way to declare a class which
is visible only in the implementation of another class is to declare
the "hidden" class as a nested class in the declaration of the class
using it.  This approach causes programmers some annoying scoping problems,
and also reduces the amount of information hiding available.  In many cases,
it is neither necessary nor desirable to define or declare these internal
classes in a publicly exported header file.

In such cases, a class with file scope and internal linkage is ideal,
but there is no way to declare such a thing.  The most logical syntax
for such a declaration would be something along the lines of "class static
classname ..."  While this syntax is not exceptionally pretty, it is no
worse than much of what we already have, is unambiguous, and provides
useful added functionality.  It seems like a natural thing to do.  Is
there in fact a reason no syntax like this is supported?

Thanks,
- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |




Author: b91926@fnclub.fnal.gov (David Sachs)
Date: 17 Jun 1993 15:42:03 GMT
Raw View
In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, daniels@NeoSoft.com (Brad Daniels) writes:
|> Section 3.3 of the working document makes explicit mention of the fact
|> that it is not possible to declare a class name static.  What is the
|> rationale behind this restriction?  Clearly, people were aware of it,
|> since it is explicitly mentioned, but its lack seems something of a
|> hole in the language.  At present, the only way to declare a class which
|> is visible only in the implementation of another class is to declare
|> the "hidden" class as a nested class in the declaration of the class
|> using it.  This approach causes programmers some annoying scoping problems,
|> and also reduces the amount of information hiding available.  In many cases,
|> it is neither necessary nor desirable to define or declare these internal
|> classes in a publicly exported header file.
|>
|> In such cases, a class with file scope and internal linkage is ideal,
|> but there is no way to declare such a thing.  The most logical syntax
|> for such a declaration would be something along the lines of "class static
|> classname ..."  While this syntax is not exceptionally pretty, it is no
|> worse than much of what we already have, is unambiguous, and provides
|> useful added functionality.  It seems like a natural thing to do.  Is
|> there in fact a reason no syntax like this is supported?
|>
|> Thanks,
|> - Brad
|> --
|> Brad Daniels  ` |  "If money can't buy happiness,
|> daniels@neosoft.com  |   I guess I'll have to rent it."
|> I don't work for NeoSoft, and |  - Weird Al Yenkovic
|> don't speak for my employer. |


Why don't you submit a formal proposal to the standards committee? I would like to see such a feature, but there are obvious problems:

What happens if a static class overrides virtual functions of an inherited non-static class?

What happens if a non-static class defines a static class as a base.

...

It probably would be necessary to require that class hierarchies be either entirely non-static or entirely static.




Author: tob@world.std.com (Tom O Breton)
Date: Fri, 18 Jun 1993 04:27:16 GMT
Raw View
>In article <C8qDu4.6Jw@sugar.NeoSoft.COM>, daniels@NeoSoft.com (Brad Daniels) writes:
>|> Section 3.3 of the working document makes explicit mention of the fact
>|> that it is not possible to declare a class name static.  What is the
>|> rationale behind this restriction?  Clearly, people were aware of it,
>|> since it is explicitly mentioned, but its lack seems something of a
>|> hole in the language.  At present, the only way to declare a class which
>|> is visible only in the implementation of another class is to declare
>|> the "hidden" class as a nested class in the declaration of the class
>|> using it.  This approach causes programmers some annoying scoping problems,
>|> and also reduces the amount of information hiding available.  In many cases,
>|> it is neither necessary nor desirable to define or declare these internal
>|> classes in a publicly exported header file.
>|>
>|> In such cases, a class with file scope and internal linkage is ideal,
>|> but there is no way to declare such a thing.  The most logical syntax

Am I misunderstanding your proposal?

It appears to me that what you want can be accomplished with not
effort by simply *not* putting the class definition in a shared header
file, as is usually done.

Is there something that would leave out?

 Tom
--
The Tom spreads its huge, scaly wings and soars into the sky...
(tob@world.std.com, TomBreton@delphi.com)