Avoid global variables/methods name clashes when using C headers in C++
I recently spent some time chasing an annoying little bug and I'm looking for suggestions for those of you who have either encountered the same problem or know the best way to avoid it.
I have a situation where I am developing in C++ and using strerror
and as a result I am using something similar to
extern "C" {
#include <string.h>
}
(Same situation for #include <cstring>
, btw). Now, there is a function defined in that file as follows: extern char *index (__const char *__s, int __c)
This function lead to the fun I had where I originally had a construct similar to:
for (int index = 0; index != condition (); ++index) {
// do something wi开发者_StackOverflowth buffer[index] here
}
log->write ("Final value of index: %d\n", index); // <- Ooops!!!
But instead of getting a compile error I get bogus output. I have my compiler (g++) flags set pretty high and the following did not catch this:
-W -Wall -Wextra -Werror -Wshadow -Wformat -pedantic -ansi
I can also not use an #undef
trick like in <cstring>
because this is not a macro.
My question is whether or not others have encountered this same problem and what is the best solution to it? Ideally, I'd love to hear about some obscure g++ functionality like -use-the-force-luke=...
;)
Note that I'm not asking how to solve this exact problem; I could just change the variable name. I'm looking for tips on how to avoid this situation in the future.
EDIT:
Due James Curran's reply I think I should clarify a bit. I'm not looking at why this should not happen. I understand that in the absence of local variables the scope space is extended. What I am surprised about is that there is no flag I can set that warns about this. I'd think that -Wshadow
would catch it since it catches variable/method shadowing within a class scope, but I digress.
What I am interested in is a way to have a notification that a local name is in conflict with a non-local scope. There is mention that I would have caught this particular bug had I used stream operations instead of variadic calls. True enough, but even the following will not produce warnings/errors with g++ (GCC) 4.1.1 20070105 (Red Hat 4.1.1-51)
and the following flags -W -Wall -Wextra -Werror -Wshadow -ansi -pedantic
.
#include <iostream>
#include <cstring>
int main () {
int index = 42;
std::cerr << index << std::endl;
return 0;
}
That is curious to me.
First, it looks like you are using printf style variadic argument list which causes an immediate loss of type safety. You should probably avoid this sort of design in C++.
If you have to do this, then you could consider decorating the function declaration to tell gcc that it is a printf-like function and it will then give you warnings if your argument list doesn't match your format string as it does for the standard *printf
functions.
E.g.
void write(const char* f, ...) __attribute__((format (printf, 2, 3)));
This is just a result of C++ scope rules - the following code is fine:
int f() {
return 42;
}
int main() {
for ( int f = 0; f < 10; f++ ) {
}
}
I don't think you will be able to get the compiler to produce a diagnostic for this - it is generally seen as desirable!
Yes, this is an annoying problem esp. with OS headers like <windows.h> or <Cocoa.h>, where frequent usage of macros or global namespace pollution is the norm. There isn't much you can do about it other than being more careful with your coding, though I found that using the definition checkup feature in a decent IDE helps out in identifying where names are defined.
Let's look at this a step at a time;
- You include a header which defines a identifier you don't know about. (unavoidable)
- You use that same identifer as a different type in you code (legal, and presumably acceptable to you)
- You accidentally use that variable outside it's scope (unfortuante but occasional occurance)
- Instead of giving you an error for the above, the compiler uses a identifer defined in the outer scope. (exactly what it's supposed to do per the Standard).
So, the question really becomes, "What were you expecting it to do?" "and "How can we implement that without breaking lots of existing code?"
Actually, the real question is, "Why are you having this problem with <cstring>
, which wraps the functions in namespace std?" -- meaning the function would actually be called std::index(). You didn't completely undermine that by adding:
using namespace std;
精彩评论