Topic: Language support of precompiled header
Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/04/24 Raw View
In article 6F56@ix.netcom.com, "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com> writes:
>
>One change that would make it less necessary to use #defines would be to
>allow any declaration that wasn't also a definition to be repeated.
It is already the case that you can repeat a declaration
that is not also a definition. You can also repeat a typedef.
Although we often use the terms "declaration" and "definition"
interchangeably for something like
struct S { int i; };
the draft standard is careful to call that a definition --
after all, it defines, not merely declares, a type and a tag.
Bearing that in mind, multiple definitions are not allowed,
except for typedefs.
> I see lots of crud like this in headers:
>
>#ifndef _DIV_T
>#define _DIV_T
>typedef struct {
> int quot;
> int rem;
>} div_t;
>#endif
That is not merely a declaration, it is a definition.
Suppose we write
struct T1 { int i; };
struct T2 { int i; };
We would not expect T1 and T2 to be the same type. They are two
different types that happen to look alike internally. (Some
languages would consider them to be the same type, using
type layout as the criterion. That is not the case in C or C++.)
Now suppose we write
typedef struct { int i; } S1;
typedef struct { int i; } S2;
Types S1 and S2 are not the same type, as above. The absence
of struct tags doesn't mean that the two structs become
the same type.
So if we repeated the typedef for div_t in the same translation
unit, it would an illegal redefinition of the type name div_t,
not merely a redeclaration of div_t or its typedef. You can
repeat a typedef as long as it defines the type name to be
the same type. You cannot redefine it to be a different type.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/04/24 Raw View
Steve Clamage wrote:
>
> Although we often use the terms "declaration" and "definition"
> interchangeably for something like
> struct S { int i; };
> the draft standard is careful to call that a definition --
> after all, it defines, not merely declares, a type and a tag.
> Bearing that in mind, multiple definitions are not allowed,
> except for typedefs.
Thanks for that correction. What I meant by definition was really
"object definition", or "definition of something that occupies memory".
I see no harm in allowing classes or enums to be defined multiple times,
as long as their definitions remain the same.
> Suppose we write
> struct T1 { int i; };
> struct T2 { int i; };
> We would not expect T1 and T2 to be the same type. They are two
> different types that happen to look alike internally. (Some
> languages would consider them to be the same type, using
> type layout as the criterion. That is not the case in C or C++.)
>
> Now suppose we write
> typedef struct { int i; } S1;
> typedef struct { int i; } S2;
> Types S1 and S2 are not the same type, as above. The absence
> of struct tags doesn't mean that the two structs become
> the same type.
I guess what you're saying is if we then wrote:
typedef struct { int i; } S;
typedef struct { int i; } S;
we'd be just as obliged to regard these as two different types as we
were in the previous case when they were given two different names, thus
causing an error. And I wouldn't change that.
But the compiler could treat:
struct T { int i; };
struct T { int i; };
different from the tagless case. It could specifically treat two structs
(or classes, unions or enums) in the same scope with the same tag as
illegal _unless_ their definitions are textually identical, in which
case they are simply harmlessly redundant. This is, after all, how
classes are usually defined in C++; they aren't usually defined to be
tagless and then given a name through typedef.
Having done that, it would then be simple to treat:
typedef struct T { int i; } S;
typedef struct T { int i; } S;
as harmlessly redundant as well, since the two "struct T { int i; }"
definitions are interpreted as the same type. One wouldn't actually do
the above, since S would be no different from T, but it would allow the
common locution:
typedef struct T { int i; } *PT;
Once those changes were in place, it would eliminate great gobs of
preprocessor crud, which might be worthwhile. Or can you think of any
gotchas?
--
Ciao,
Paul
(Please send e-mail to mailto:pderocco@ix.netcom.com instead of the
return address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/04/25 Raw View
In article 606A@ix.netcom.com, "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com> writes:
>
>But the compiler could treat:
>
> struct T { int i; };
> struct T { int i; };
>
>different from the tagless case. It could specifically treat two structs
>(or classes, unions or enums) in the same scope with the same tag as
>illegal _unless_ their definitions are textually identical, in which
>case they are simply harmlessly redundant.
"Textually identical" is both too strong and too weak a criterion,
even assuming we make the comparison after preprocessing.
Examples where it is too strong:
typedef int INT;
struct T1 { int i; };
struct T1 { INT i; }; // OK?
struct T2 { int i, j; };
struct T2 { int i; int j; }; // OK?
enum E { a=1, b=2 };
enum E { b=2, a=1 }; // OK?
These T1, T2, and E sets are not textually identical, but if the original
repeated definitions above are allowed, shouldn't these also be allowed?
If not, why not? The T1's, T2's, and E's really are "the same", in the
sense that changing the definition from one to the other can have no
effect on any program on any platform.
Example where the "textually identical" criterion is too weak:
extern int f(int);
struct T3 {
int g() { return f('a'); }
};
extern int f(char); // new overload for f
struct T3 {
int g() { return f('a'); }
};
The second version of T3 is character-for-character identical to
the first, yet member function g has differing semantics. If you
disallow function definitions to avoid this problem, you lose
the ability to repeat class definitions that involve inline
functions, and you are back to needing conditional-compilation
guards.
And what about default parameter values? Example:
struct T4 {
T4(int = 0);
};
struct T4 {
T4(int = 0);
};
These are identical, but violate the rule about repeating default
parameter values. Do we somehow change that rule? If so, how?
As I said before, some languages consider types to be the same
if they have the same structure. The designers of C chose not to use
that model, due to the complications it entails. In C++, deciding
whether two definitions are the same is much more complicated.
(And we haven't even talked about templates and partial specialization
yet!)
I don't think allowing selected violations of the One-Definition Rule
yields any net benefit.
In my experience, the "#ifdef crud" you were complaining about
shows up mainly in the C and C++ standard header files. That is
merely one possible implementation technique. Nothing in the standards
requires those headers to look like that.
I don't recall ever needing to use fine-grained guards in application
code. Usually there is one set of guards around the entire file, or
around #include directives. If you do need fine-grained guards, I
would suspect you could find a better way to factor the design
and implementation of the application.
I think suggestions along the lines of an "import" directive
(include only if not previously included) addresses the real
concern about needing guards at all.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/04/28 Raw View
Steve Clamage wrote:
>
> "Textually identical" is both too strong and too weak a criterion,
> even assuming we make the comparison after preprocessing.
>
> Examples where it is too strong:
> typedef int INT;
> struct T1 { int i; };
> struct T1 { INT i; }; // OK?
>
> struct T2 { int i, j; };
> struct T2 { int i; int j; }; // OK?
>
> enum E { a=1, b=2 };
> enum E { b=2, a=1 }; // OK?
>
> These T1, T2, and E sets are not textually identical, but if the original
> repeated definitions above are allowed, shouldn't these also be allowed?
> If not, why not? The T1's, T2's, and E's really are "the same", in the
> sense that changing the definition from one to the other can have no
> effect on any program on any platform.
The same argument could be made about #define's. Why can't:
typedef int INT;
#define declareint(x) int x;
#define declareint(x) INT x;
coexist? It's no more of a hardship to require that all versions of the
tagged class definition be an identical token sequence than to require
all definitions of a #define to be an identical token sequence. The only
thing I'm trying to accomplish is to make it _possible_ to include the
same stuff in multiple headers without having to resort to the
preprocessor.
> Example where the "textually identical" criterion is too weak:
> extern int f(int);
> struct T3 {
> int g() { return f('a'); }
> };
> extern int f(char); // new overload for f
> struct T3 {
> int g() { return f('a'); }
> };
> The second version of T3 is character-for-character identical to
> the first, yet member function g has differing semantics. If you
> disallow function definitions to avoid this problem, you lose
> the ability to repeat class definitions that involve inline
> functions, and you are back to needing conditional-compilation
> guards.
Hmmm. I'm not surprised that it's possible to construct examples like
that. The ambiguity comes as a result of the overloaded function
definition following a use of that function, and I'm not sure it's ever
really necessary to do that. It's certainly troublesome. If it isn't
necessary, then I wouldn't mind if the compiler complained about the
change in meaning (in which case, the designer of the header would have
to go back in and rearrange things to avoid the ambiguity), although I'd
rather it simply reused the first definition, despite the intervening
overload (which is probably what is intended).
> And what about default parameter values? Example:
> struct T4 {
> T4(int = 0);
> };
> struct T4 {
> T4(int = 0);
> };
> These are identical, but violate the rule about repeating default
> parameter values. Do we somehow change that rule? If so, how?
Since what I'm trying to accomplish is to allow the same class
definition, having the same meaning, to be included more than once, I
think the cleanest way to modify the rule is simply to say that the
redefinition of a class that has a tag is illegal, unless it is an
identical token string from the word "class" (or "struct", "union" or
"enum") through the close brace. If something intervening has changed
the meaning, the program is "ill-formed," although the compiler isn't
required to provide a diagnostic for this case.
> I don't think allowing selected violations of the One-Definition Rule
> yields any net benefit.
The benefit is the elimination of preprocessor conditional compilation.
The reason it's particularly useful to get rid of that is that
conditional compilation can hack right across parse trees, making it (in
the general case) impossible to precompile as far as you otherwise
could. For instance, the statement:
if (k) x = y + z;
else x = y - z;
can be precompiled all the way to the level of a parse tree (assuming
you don't know what k is yet), while
#if k
x = y + z;
#else
x = y - z;
#endif
can't, because in the general case #ifdefs allow uglier constructs, such
as:
x = y
#if k
+
#else
-
#endif
z;
Yeah, I know that's a pretty pathological example, but it's certainly
the case that #ifdef is sometimes used in peculiar ways that can't be
postponed to later stages of compilation. Therefore, anything that
steers the programmer into writing stuff that's part of the "true" C++
language (after preprocessing) is, in the words of Martha Stuart, a Good
Thing. (This is analogous to the preferability of using "const" instead
of "#define".)
> I think suggestions along the lines of an "import" directive
> (include only if not previously included) addresses the real
> concern about needing guards at all.
Hear, hear! It amazes me that they still haven't added that to the
language. (But they still haven't added binary numeric literals,
either.)
--
Ciao,
Paul
(Please send e-mail to mailto:pderocco@ix.netcom.com instead of the
return address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/04/28 Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) writes:
|> Suppose we write
|> struct T1 { int i; };
|> struct T2 { int i; };
|> We would not expect T1 and T2 to be the same type. They are two
|> different types that happen to look alike internally. (Some
|> languages would consider them to be the same type, using
|> type layout as the criterion. That is not the case in C or C++.)
This is indiscutably true in C++, but I think in C, there are a few
gotcha's. (The expression the C standard uses is "compatible
declarations", if I remember correctly.) In particular, C uses
structural equivalence between compilation units; the above declarations
*might* be the same type in C if they were in different compilation
units.
Since this is a C++ forum, not a C forum, I won't go into the gory
details. But it is interesting to note that we've actually a case here
where C++ is simpler than C!
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/04/29 Raw View
In article fsf@vx.cit.alcatel.fr, James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
>
>Stephen.Clamage@eng.sun.com (Steve Clamage) writes:
>
>|> Suppose we write
>|> struct T1 { int i; };
>|> struct T2 { int i; };
>|> We would not expect T1 and T2 to be the same type. They are two
>|> different types that happen to look alike internally. (Some
>|> languages would consider them to be the same type, using
>|> type layout as the criterion. That is not the case in C or C++.)
>
>This is indiscutably true in C++, but I think in C, there are a few
>gotcha's. (The expression the C standard uses is "compatible
>declarations", if I remember correctly.) In particular, C uses
>structural equivalence between compilation units; the above declarations
>*might* be the same type in C if they were in different compilation
>units.
They aren't really the same type, but they are compatible types in
both C and C++, as exemplified here:
file1
======
struct T1 { int i; } t1;
extern "C" int f(T1*);
int g() { return f(&t1); }
file2
======
struct T2 { int i; }; // compatible with T1
extern "C" int f(T2* t2) { // compatible with f in file1
return t2.i;
}
You can compile these modules and link them together and the program
will work. In C++ layout-compatibility applies to POD-structs and
POD-unions, not to general classes.
I gave function f C linkage, otherwise the program wouldn't work.
The equivalent C program does work. That is, the declaration for
function f in file1 is compatible with the declaration for function f
in file2, because
- T1 and T2 are layout-compatible
- the compiler doesn't see both declarations for f at the same time
A program would be invalid in C and C++ if both declarations for f were
visible at the same time, because T1 and T2 are not the same type.
(Without C linkage, you would have overloaded versions of f in a
C++ program.)
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]