Reputation: 209
I am not particularly new to C/C++ but today I discovered some things that I didn't expect. This compiles in gcc:
/* test.c */
#include <stddef.h> // !
typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR
int
main(void)
{
typedef unsigned long int size_t; // NO ERROR
return 0;
}
This doesn't:
/* test.c */
#include <stddef.h>
typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR
int
main(void)
{
typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // ERROR
return 0;
}
This doesn't either:
/* test.h */ // ! header
typedef unsigned long int size_t;
typedef unsigned long int size_t; // ERROR
Similarly in g++ this compiles:
/* test.h */ // ! header
#include <cstddef>
inline void* operator new(size_t, void* p) throw() { return p; }
This doesn't:
/* test.h */ // ! header
#include <new> // !
inline void* operator new(size_t, void* p) throw() { return p; } // ERROR
This does:
/* test.cc */
#define _NEW
#include <new> // !
#include <iostream>
#include <cstdlib>
using std::cout;
using std::endl;
inline void* operator new(size_t size) throw() // NO ERROR EXPECTED
{
cout << "OPERATOR NEW CALLED" << endl;
return malloc(size);
}
inline void* operator new(size_t, void* p) throw() // NO ERROR
{
cout << "PLACEMENT NEW CALLED" << endl;
return p;
}
int main()
{
char *buffer[4];
int *i = new (buffer) int;
int *j = new int;
return 0;
}
(Replacing the standard new operator works in all of the above cases. Replacing the replacement new operator is illegal, I know.)
It is easy to see a pattern, but can somebody offer me a "standard" explanation? Why can I do things in .c or .cc files that I can't in .h files (redefine old typedefs, replace functions that are illegal to replace)?
Thanks for the reply. I have omitted some code including the header guards. In the last .cc example, I HTML encoded << as & g t ; & g t ; by mistake and forgot to include cstdlib. I have fixed the code so it compiles. However, another thing that I had omitted was #define _NEW, which proved crucial. Apparently in GNU the header guard of <new> defines _NEW, so my defining it here prevented the inclusion of the standard new header, so I the replacement worked
m@m:~/Desktop/Library$ ./a.out PLACEMENT NEW CALLED OPERATOR NEW CALLED
So yeah, the only thing that is left unexplained is how come I can re-typedef stuff in .c/.cc files multiple times but not in .h like so:
/* test.c */
#include <stddef.h>
typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR
int
main(void)
{
typedef unsigned long int size_t; // NO ERROR
return 0;
}
Not that I want to do that anyway, but just wondering.
EDIT: Thanks, that really answers all of it. Changing it to xyz does not allow multiple definitions in a given scope, which feels right. :) The reason why I was doing these little tests is that I am writing a subset of C and C++ for a small operating system but since I am using the existing libraries without removing anything to assist me in the testing of mine, I was trying to figure out how to ensure that (1) my code is being called in the tests (e.g my placement new) and (2) that my code doesn't clash with the GNU libraries. The answer to (1) now seems pretty obvious. I just #define the header guard macro from the standart X.h into my X.h :).
Upvotes: 2
Views: 5036
Reputation: 754700
Using GCC 4.0.1 on MacOS X 10.4.11 (ancient - but so's the computer), the example with "test.h" works - or my adaptation of it does. It appears you can have as many (identical) global typedefs of 'size_t' as you like - I had 5 with the version in stddef.h
.
The first typedef inside main is 'obviously' legal; it is a new scope and can define a new meaning for the name.
The C99 Rationale says:
In C89, a typedef could be redeclared in an inner block with a declaration that explicitly contained a type name. This rule avoided the ambiguity about whether to take the typedef as the type name or a candidate for redeclaration. In C99, implicit int declarations are not allowed, so this anbiguity [sic!] is not possible and the rule is no longer necessary.
Later on, discussing standard headers, it also says:
The C89 Committee decided to make library headers “idempotent,” that is, they should be includable any number of times, and includable in any order. This requirement, which reflects widespread existing practice, may necessitate some protective wrappers within the headers to avoid, for instance, redefinitions of typedefs. To ensure that such protective wrapping can be made to work, and to ensure proper scoping of typedefs, standard headers may only be included outside of any declaration.
Clearly, therefore, redefinitions of a typedef
in a single scope (e.g. the file scope) is not allowed in general.
I think, therefore, that the multiple external redefinitions of size_t
is probably either a feature of GCC or a bug - actually, a feature.
size_t
to xyz
, you get many more errors.
Upvotes: 3
Reputation: 111250
I am not sure how the multiple typedef
s compile in your case. Remember, headers don't compile by themselves, but in conjunction with an implementation class. Also, your headers lack header guards or a #pragma once
directive. What options are you using?
As for placement new
-- it is explicitly forbidden by the standard (18.5.1.3) -- so that won't work.
There is no pattern here -- except for redefinition of already declared symbols.
BTW: None of your .c
or .cpp
examples compile with Comeau.
Upvotes: 2