views:

112

answers:

4

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 with 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.

+2  A: 

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!

anon
+7  A: 

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)));
Charles Bailey
For more on GCC's attribute syntax, see http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html.
Josh Kelley
While this does catch the problem I describe, it forces me to drop the `-pedantic` flag (no `%zd` support). Maybe that's a choice that has to be made...?
ezpz
Why not simply return an istream reference?
DeadMG
A: 

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.

5ound
A: 

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;
James Curran
Actually, `<cstring>` does _not_ do this. It gets rid of macro definitions and names them `::macro` in the `std` namespace but simply does `#include <string.h>` before any of that happens. And, I always use `std::` instead of `using namespace std` so that is not an issue.
ezpz
well G++'s cstring may not, but if so, it's wrong. The purpose of the c????? headers was mainly to put the C stdlib functions into a namespace.
James Curran