Reputation: 2631
Suppose I have a macro defined as this:
#define FOO(x,y) \ do { int a,b; a = f(x); b = g(x); y = a+b; } while (0)
When expanding the macro, does GCC "guarantee" any sort of uniqueness to a,b? I mean in the sense that if I use FOO in the following manner:
int a = 1, b = 2; FOO(a,b);
After, preprocessing this will be:
int a = 1, b = 2; do { int a,b; a = f(a); b = g(b); b = a+b; } while (0)
Can/will the compiler distinguish between the a
outside the do{} and the a
inside the do? What tricks can I use to guarantee any sort of uniqueness (besides making the variables inside have a garbled name that makes it unlikely that someone else will use the same name)?
(Ideally functions would be more useful for this, but my particular circumstance doesn't permit that)
Upvotes: 5
Views: 22547
Reputation: 1356
You can actually make sure that regardless of what x and y are you won't have this problem by doing the following:
#define FOO(x,y) \
do\
{\
int x##y##a,x##y##b;\
x##y##a = f(x);\
x##y##b = g(x);\
y = x##y##a + x##y##b;\
} while (0)
By making sure a and b names contains x and y names you know they are different. But that's a really bad code and you probably shouldn't code like this.
Upvotes: 1
Reputation: 20741
If you are targeting gcc and/or g++ then you can use their special macro block feature:
#define max(x, y) ({ typeof(x) a_ = (x); \
typeof(y) b_ = (y); \
(a_ > b_) ? a_ : b_ })
This allows you to create unique local variables, very much the same as writing a function.
Of course, for portability, it's not recommended. On the other hand, if you only plan to work on systems that offer gcc/g++, it will work on all of those.
Source: http://gcc.gnu.org/onlinedocs/gcc-3.0.1/cpp_3.html#SEC30
Further, with gcc / g++ you can use the -Wshadow
command line option. In the event you inadvertently reuse a local variable with the same name, it will warn you. You can further use -Werror
to transform those warnings in error. Now you can't compile if there is the possibility of a mixed up variable. You need to make sure to use a block, though. A do/while() as others have presented would do the job.
int a;
// code from macro;
do { int a = 5; ... } while(false);
With the combo I just described (-Wshadow
+ -Werror
), you get an error when you do int a = 5
.
Upvotes: 1
Reputation: 4191
Macros perform just string substitution. The semantic is low and the the compiler have a limited knowledge of the preprocessor (essentially #pragma which in fact is not a preprocessor keyword, and source line info).
In your case a and b are not initialized local value. Behavior is unpredictible. Your expanded code is equivalent to the following one.
int a = 1, b = 2;
do {
int a___,b___;
a___ = f(a___);
b___ = g(b___);
b___ = a___+b___;
} while (0)
To avoid such case in c++ prefer the use of inline function or template. If you use a c 1999 compliant compiler, you can use inline in c language. http://en.wikipedia.org/wiki/Inline_function
In c you can make safer macro by defining longer variable and surrounding parameter by () :
#define FOO(x,y) \
do {
int FOO__a,FOO__b;
FOO__a = f(x);
FOO__b = g(x);
y = FOO__a+FOO__b + (y)*(y);
} while (0)
Note : I changed your example by adding a (y)*(y) to illustrate the case
It is also a good practice to use only once macro parameter. This prevent side effects like that:
#define max(a,b) a>b?a:b
max(i++,--y)
Max will not return what you want.
Upvotes: 6
Reputation: 31445
In your macro that is indeed a danger and so is the possible reuse of x
which is not guaranteed to be an l-value and could change by being used in the macro twice.
Even if you do need a macro, it can still be a light wrapper around an inline function, and one such that will take x and give you both f(x)
and g(x)
without possibly having to re-evaluate x would certainly be safe.
In your case something like:
template< typename T >
struct Foo
{
T& x;
explicit Foo(T&x_) : x(x_)
{
}
int f();
int g();
};
template<typename T>
Foo<T> makeFoo(T& x)
{
return Foo<T>(x);
}
#define FOO(x,y)
{
Foo FOO_VAR(x);
y = FOO_VAR.f() + FOO_VAR.g();
}
would be a safer way to do things. Of course if you don't need the macro at all, do away with it.
Upvotes: 0
Reputation: 213920
Variables a and b are treated just as any local variables inside a local scope.
The C language guarantees that if those variables happen to have the same names as outer scope variables, the local variables will be the ones updated.
Here is an example to illustrate:
#include <stdio.h>
#define FOO(x) \
{ \
int a; \
a = x; \
printf("%d\n", a); \
}
int main()
{
int a = 1;
{
int a = 2;
printf("%d\n", a); // 2
FOO(3); // 3
printf("%d\n", a); // 2
}
printf("%d\n", a); // 1
getchar();
}
Now, of course it might be a bright idea to not name every single variable in your program "a" just because C guarantees that local variables take precedence. But technically there is nothing stopping you from it.
Btw MISRA-C bans naming like this, it require each variable no matter scope to have an unique name, for readability and maintenance reasons.
(As a sidenote, function-like macros is incredibly poor programming style and shouldn't be used. Use real functions instead, and inline them if performance is critical.)
Upvotes: 4
Reputation: 52294
There is no tricks other than garbling. The C and C++ preprocessors do not have the equivalent of lisp gensym or hygienic macros.
Upvotes: 3
Reputation: 16406
Macros are textually expanded as is, modulo parameter replacement, so there's nothing the compiler can do to provide the sort of guarantee you're asking for -- as you can see in the expansion, the a
parameter will refer to the inner a
, not the outer one. The solution is indeed to use "garbled" names, e.g. int FOO_a, FOO_b;
Upvotes: 0
Reputation: 8340
Nope, there is no guarantee of uniqueness.
Infact, your code is about to fail.
Macros are just like replacement of text.
I usually use crazy variable names if I am inside a macro, like this:
#define FOO(x,y) \
do {
int FOO_MACRO_a, FOO_MACRO_b;
FOO_MACRO_a = f(x);
FOO_MACRO_b = g(x);
y = FOO_MACRO_a + FOO_MACRO_b;
} while (0)
Upvotes: 1
Reputation: 22104
If we consider scoping of variables, it is guaranteed that a,b inside the do..while() will be different from the ones defined outside.
For your case, the a,b defined outside will not exist inside the do..while().
There are lots of things to watch out for when using MACROs.
Upvotes: 7