views:

2010

answers:

3

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)?

+2  A: 

I am not sure how the multiple typedefs 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.

dirkgently
A: 

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 :).

Martin
Just remember that there is no standard for the name of the header guards used by the implementation. Although G++ may use one convention, other compilers probably used different conventions.
Jonathan Leffler
+3  A: 

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.

If you change size_t to xyz, you get many more errors.
Jonathan Leffler
Interesting info, but please note that the question is marked "C++" -- not sure if C89/C99 technicalities are honoured.
j_random_hacker
The question is titled "C/C++ ...", and the first half of the question specifically asks about 'gcc' and not 'g++'. The second half asks about C++.
Jonathan Leffler
Whoops, you're quite right. +1.
j_random_hacker