5

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)

R.D.
  • 2,471
  • 2
  • 15
  • 21
  • 1
    http://en.wikipedia.org/wiki/Hygienic_macro – Josh Lee Mar 01 '11 at 09:20
  • 5
    Don't use macros for things like that. We have functions for a reason. – ThiefMaster Mar 01 '11 at 09:26
  • @jleedev: Hygienic macros sound like a great idea...why hasn't gcc implemented this? It sounds like the implementation would be simple straightforward. @ThiefMaster: Agreed that functions would be ideal for this, but they're out of the question because I can't add public functions for this purpose. – R.D. Mar 01 '11 at 09:33
  • You can't write an exactly equivalent function, but you can write one the returns a+b rather than storing it in the y parameter. Why don't your circumstances allow that? – Jim Balter Mar 01 '11 at 09:44
  • 1
    @R.D. gcc is a C compiler and implements what the C Standard specifies. It also implements some extensions, but these encourage people to write non-portable code; they should be avoided. – Jim Balter Mar 01 '11 at 09:47
  • You have pretty much answered your own question. The code will indeed end up as in your second block and no it won't see the a and b outside so the a in f(a) and the b in g(b) are uninitialised variables. Pick variable names that do not meet your normal coding standard to avoid it. Something like MV_a MV_b. – CashCow Mar 01 '11 at 10:27
  • @ThiefMaster Yes a good reason to avoid macros but sometimes you want them, eg if you are going to optionally compile out the code. – CashCow Mar 01 '11 at 10:28
  • @Cashcow You can of course optionally compile out the code in functions as well. No need to use macros for that! – Bo Persson Mar 01 '11 at 18:12

9 Answers9

7

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.

Shamim Hafiz - MSFT
  • 21,454
  • 43
  • 116
  • 176
6

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.

VGE
  • 4,171
  • 18
  • 17
  • 2
    In C, make a (inline) function. Don't use function-like macros ever. – Lundin Mar 01 '11 at 10:39
  • But macros are usefule when conditional compilation is required. An inline function gets called, and has it's parameters evaluated, which can be meaningless or a performance downer. Never, suggests you never had to do a real world port of a significant system, between differing environments. – Rob11311 Jun 20 '14 at 16:26
  • @Rob11311 That an inline function gets called and causes performance issues suggests that you are not using the correct compiler flags. – L. F. Jun 16 '19 at 11:55
4

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

Lundin
  • 195,001
  • 40
  • 254
  • 396
3

There is no tricks other than garbling. The C and C++ preprocessors do not have the equivalent of lisp gensym or hygienic macros.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
1

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)
bits
  • 8,110
  • 8
  • 46
  • 55
  • Do you know how the namespace rules in C work? If you declare a local variable X and happen to have a variable also named X in another scope, your local variable will be the one that gets changed. There is absolutely no need to obfuscate the naming of your local variables. So the code is *not* about to fail, the only reason for naming local variables different than variables in outer scopes is readability. – Lundin Mar 01 '11 at 10:25
  • 1
    @Lundin: Scoping and namespaces are separate things, please do not confuse the terminology. – Christopher Creutzig Mar 01 '11 at 15:40
  • @Chistopher Well... yes and no. "Scope" is a language nerd term used in the standards, while "namespace" is the term that the average programmer uses when speaking about programming on a generic level (language-independent). In the C language, namespaces aren't mentioned at all by the standard, so for a C programmer namespace and scope are the same thing. In C++ they are indeed entirely different things and shouldn't be mixed up. – Lundin Mar 01 '11 at 15:49
  • @Lundin What is going to fail is `a = f(x);` when x happens to be the a from the outer scope. Expanding it to `a = f(a);` surely fails. – Bo Persson Mar 01 '11 at 18:19
1

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.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • No need for do while you can just do `some code {some code in block} some more code` without do while. – Tomer Wolberg May 16 '19 at 13:20
  • @TomerWolberg See chosen answer here https://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block — it's not _always_ required. – Alexis Wilke May 16 '19 at 16:38
1

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.

Tomer Wolberg
  • 1,256
  • 10
  • 22
  • 1
    Interesting suggestion! Yet there are still 2 potential problems: Unlike the original one, this new macro cannot be used with an expression as the first argument and you should remove the `;` at the end of the macro. – chqrlie Jun 15 '19 at 22:23
  • @chqrlie Your'e right, and I removed the `;`. And I'm afraid it's impossible to solve the first problem. – Tomer Wolberg Jun 16 '19 at 11:42
  • I guess one could use a single variable, namely an array of 2 ints, but I don't see a way to name it that is guaranteed not to clash with whatever `x` uses either. Naming it `y##y` makes it different from `y`, but `x` can be any name and or expression, hence can include any identifier. I agree with your conclusion: *the OP probably shouldn't code like this.* – chqrlie Jun 16 '19 at 20:33
0

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;

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
0

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.

CashCow
  • 30,981
  • 5
  • 61
  • 92