tags:

views:

99

answers:

5

I'd like to define a function like MACRO . i.e.

#define foo(x)\
#if x>32\
 x\
#else\
 (2*x)\
#endif

that is,

if x>32, then foo(x) present x
else, foo(x) present (2*x)

but my GCC complains about:

int a = foo(31);

I think C preprocessor should be handle this correctly. since at compile time, it knows x=33. it could replace foo(33) with (2*33)

+1  A: 

Consider:

int x = rand()
int y = foo( x );

x is not known at compile time.

anon
+3  A: 

You can as follows

#define foo(x) ((x) > 32 ? (x) : (2 * (x)))

But that evaluates x multiple times. You can instead create a static function, which is cleaner

static int foo(int x) {
  if(x > 32) 
    return x;
  return 2 * x;
}

Then you are also able to pass things to foo that have side effects, and have the side effect happen only one time.

What you have written is using the #if, #else and #endif preprocessor directives, but you need to use language constructs if you pass variables to the macro and want to evaluate their values. Using if, and else statements as in the actual language constructs don't work either, because control flow statements don't evaluate to values. In other words, an if statement is steering control flow only ("if A, then execute B, else execute C"), not evaluating to any values.

Johannes Schaub - litb
Surely x is evaluated twice in both cases?
anon
@Neil the argument (not the parameter) is only evaluated once in the function case. Try calling `int i = 0; foo(i++);` For the function case, i will be `1` afterwards. But for the macro case, it will be `2`.
Johannes Schaub - litb
I want C preprocessor does evaluation instaed of run time flow control. this way save time at run time.BTW why you say " #define foo(x) ((x) > 32 ? (x) : (2 * (x))) evaluates x multiple times " ?
richard
@richard: make the function inline - if the value is known at compile time, the compiler will generally perform the evaluation at compile time. Compilers may well do this even for static functions that aren't marked `inline`. You might even get the benefit of the optimization for an `extern` function with more modern compiler/linker tools (but the likelihood decreases). A quick test with VS2008 shows that it had no problem inlining litb's `foo()` function even when it was an `extern` defined in a separate source file. When passed literal ints, the results were computed at compile time.
Michael Burr
@richard: (continuing on) - with today's tools, there's really little need to worry about the kinds of 'optimization' that your question is concerned with. You generally don't need to jump though hoops to "save time at run time" using these kinds of macros/preprocessor tricks. First write the code in a manner that's correct, maintainable, and using an efficient algorithm (note: that's different than trying to count cycles). Only worry about jumping through hoops when it turns out that the initial attempt isn't fast enough.
Michael Burr
+1  A: 
int a = foo(31);

Expands out to

int a = if 31>32
31
else
(2*31)
endif;

That's how C macros work, via simple, dumb substitution. If you expect gcc to do anything more complex or intelligent with them, then your expectation is erroneous.

Given that, it's easy to see why your code won't work. An alternative that would suffice for this example would be:

#define foo(x) (x > 32 ? x : 2*x)

On the other hand, I would question whether macros are really the appropriate tool for such a thing to begin with. Just put it in the function and the compiler will inline the code if it thinks it will speed it up.

Thom Smith
The x's in the macro in my answer should be wrapped in parentheses as in Johannes's macro. Otherwise, it will fail for complex expressions. This is another reason to avoid macros – simple, innocuous errors can cause very strange bugs down the road.
Thom Smith
A: 

The problem is not about the theory: provided that you, for some reason, want to have a macro that expands differently according to the value of a parameter passed to it, and this parameter is a constant, known to the macro preprocessor, there's no reason why it couldn't work... for a generic macro processor... But cpp unluckly does not allow the presence of other macro processor "commands" into a macro definition...

So your

#define foo(x) \
#if x>32 \
  x      \
#else    \
  2*x    \
#endif

does not expand to

#if X>32
  X
#else
  2*X
#endif

where X is the known parameter (so change X to e.g. 31), which requires another pass by the preprocessor.

Moreover newlines are ignored, while they are important for such an use; otherwise, the following could be considered as a trick (that need another preprocessing pass however)

#define foo(x,y) \
y if x>32 \
  x  \
y else \ 
  2*x \
y endif

that with foo(20,#) produces

# if 20>32 20 # else 2*20 # endif

which would work, if it would be

# if 20>32
  20
# else
  2*20
# endif

... but it is not (and as said, the output of the preprocessor must be feeded to the preprocessor again...)

So my answer is that if you need these things, you can't use the C preprocessor; you should use a uncommon (not standard?) C preprocessor, or just another macro processor, and if you need the sort of things that "cpp" has to "integrate" itself with C, then you can't use a generic one (like M4) so easily...

ShinTakezou
The problem is not about the theory: provided that you, for some reason, want to have a macro that expands differently according to the value of a parameter passed to it, and this parameter is a constant, known to the macro preprocessor.Yes, you got my point. but the solution is so tricky. Is there any other way?
richard
as said, no without changing the macro (pre)processor... currently I don't know macro processor similar to cpp with the feature you need
ShinTakezou
A: 
#define \
    foo(x) \
    ({ \
        int xx = (x); \
        int result = (xx > 32) ? xx : (2*xx); \
        result; \
    })
Vanni Totaro
Have you tried using this?
Michael Burr
@Michael: for sure! On Ubuntu 10.04 with gcc. The Linux kernel macros often do the same. And I used it extensively in a Linux C++ project of mine. What does seem strange to you in my code snippet?
Vanni Totaro
@Michael: try `grep -C2 -rn '})' . | grep -v Documentation | less` on a Linux source tree and you will find the same technique used in a lot of places :-)
Vanni Totaro
No one upvoted my correct answer yet... maybe it is not so self-speaking, so later I'll edit the post adding syntax sugar explanation. :)
Vanni Totaro
@Vanni: I had forgotten that GCC has the extension called 'Statement Expressions'. Since the person who asked the question tagged it with `gcc`, this might be OK for him. However, I think a simple inline function would work as well and be more portable.
Michael Burr
@Michael: thank you for pointing me out that this is a gcc extension! I didn't know its name and that it isn't portable! :)
Vanni Totaro