This question is going to take a bit of explination, sorry. I am fixing an oversight in doxygen parsing some C++ code and I have come across an unusual corner case that doxygen doesn't account for. I have a fix but I want to make it more general so I need some explanation.
To illustrate the case where doxygen is failing I am going to define a contrived example involving The Simpsons (because that seems to be popular for these types of questions). Let's say that we have the following enum:
enum simpson { HOMER, MARGE, BART, LISA, MAGGIE };
Now we want to pass the enum value into a method (of the Simpsons class naturally) that looks like this:
const char* voicedBy(simpson simpson)
{
switch (simpson) {
case HOMER:
return "Dan Castellaneta";
case MARGE:
return "Julie Kavner";
case BART:
return "Nancy Cartwright";
case LISA:
return "Yeardley Smith";
case MAGGIE:
return "*suck* *suck*";
}
}
Unfortunately this produces a compiler error because the enum type 'simpson' is not allowed to be the same as the parameter name 'simpson' (Unlike in, say, C#). But, C++ has an answer to this. You put the enum keyword in front of the type name like this:
const char* voicedBy(enum simpson simpson)
and the code will now compile and run. Unfortunately doxygen doesn't account for this case and so it treats the entire string "enum simpson simpson" as a parameter type with no parameter name. I have come up with some code to fix doxygen in the case of an enum like above.
My question is, what other types is this kind of trick valid for? struct?, union?, typedef?, others? And for that matter, does the 'type specifier for method parameter with same name as parameter name' concept have a name so that I can get some more details on it?
What compiler and version are you using?
void foo( simpson simpson ) {}
No enum
present, that is, you don't need to use an elaborated type specifier in this context, and it compiles perfectly in gcc 4.2 and 4.6. The problem is that inside the function, the argument name hides* the type, and if you want to declare a new variable with that type inside that scope you will need the elaborated type specifier, but in the function signature it is parsed left to right, and that means that the first simpson
is the enum and at that time there is no collision. The second simpson
introduces a local name, and from there on, simpson
refers to the parameter and not the type.
void relationship( /*enum*/ simpson simpson, enum simpson other = HOMER );
// ^^^^ optional ^^^^ required
{
enum simpson yet_another = simpson;
// ^^^^ required ^^^^^^^ first argument!
}
The same type of name hiding can happen if you define a function with the same name as the type you want:
void simpson();
void voicedBy( enum simpson s );
// ^^^^ required
Note that if you add a typedef the things change a little in this last case: there will be a name clash between the typedef-ed name and the function name (or a variable name in the same scope).
* Here hides is not used in the sense of a variable in one scope hiding a variable with the same name in an outer scope. In C++, as in C, there are two identifier spaces, one for user defined types, and another for mostly everything else including typedef
-ed type aliases. Lookup in C++ is performed from the inner scope to the outer scope, and in each scope the global identifier space is searched, if the identifier is not found, then the user defined types identifier space is searched for the same identifier. This last step is not performed in C, where elaborated type specifiers are required always. Only if that also fails, the compiler will move to the next scope.