Related
I am trying to understand defining functions as macros and I have the following code, which I am not sure I understand:
#define MAX(i, limit) do \
{ \
if (i < limit) \
{ \
i++; \
} \
} while(1)
void main(void)
{
MAX(0, 3);
}
As I understand it tries to define MAX as an interval between 2 numbers? But what's the point of the infinite loop?
I have tried to store the value of MAX in a variable inside the main function, but it gives me an error saying expected an expression
I am currently in a software developing internship, and trying to learn embedded C since it's a new field for me. This was an exercise asking me what the following code will do. I was confused since I had never seen a function written like this
You are confused because this is a trick question. The posted code makes no sense whatsoever. The MAX macro expands indeed to an infinite loop and since its first argument is a literal value, i++ expands to 0++ which is a syntax error.
The lesson to be learned is: macros are confusing, error prone and should not be used to replace functions.
You have to understand that before your code gets to compiler, first it goes through a preprocessor. And it basically changes your text-written code. The way it changes the code is controlled with preprocessor directives (lines that begin with #, e.g. #include, #define, ...).
In your case, you use a #define directive, and everywhere a preprocessor finds a MAX(i, limit) will be replaced with its definition.
And the output of a preprocessor is also a textual file, but a bit modified. In your case, a preprocessor will replace MAX(0, 3) with
do
{
if (0 < 3)
{
0++;
}
} while(1)
And now the preprocessor output goes to a compiler like that.
So writing a function in a #define is not the same as writing a normal function void max(int i, int limit) { ... }.
Suppose you had a large number of statements of the form
if(a < 10) a++;
if(b < 100) b++;
if(c < 1000) c++;
In a comment, #the busybee refers to this pattern as a "saturating incrementer".
When you see a repeated pattern in code, there's a natural inclination to want to encapsulate the pattern somehow. Sometimes this is a good idea, or sometimes it's fine to just leave the repetition, if the attempt to encapsulate it ends up making things worse.
One way to encapsulate this particular pattern — I'm not going to say whether I think it's a good way or not — would be to define a function-like macro:
#define INCR_MAX(var, max) if(var < max) var++
Then you could say
INCR_MAX(a, 10);
INCR_MAX(b, 100);
INCR_MAX(c, 1000);
One reason to want to make this a function-like macro (as opposed to a true function) is that a macro can "modify its argument" — in this case, whatever variable name you hand to it as var — in a way that a true function couldn't. (That is, if your saturating incrementer were a true function, you would have to call it either as incr_max(&a, 10) or a = incr_max(a, 10), depending on how you chose to set it up.)
However, there's an issue with function-like macros and the semicolon at the end. I'm not going to explain that whole issue here; there's a big long previous SO question about it.
Applying the lesson of that other question, an "improved" INCR_MAX macro would be
#define INCR_MAX(var, max) do { if(var < max) var++; } while(0)
Finally, it appears that somewhere between your exercise and this SO question, the while(0) at the end somehow got changed to while(1). This just about has to have been an unintentional error, since while(1) makes no sense in this context whatsoever.
Yeah, there's a reason you don't understand it - it's garbage.
After preprocessing, the code is
void main(void)
{
do
{
if ( 0 < 3 )
{
0++;
}
} while(1);
}
Yeah, no clue what this thing is supposed to do. The name MAX implies that it should evaluate to the larger of its two arguments, a la
#define MAX(a,b) ((a) < (b) ? (b) : (a))
but that's obviously not what it's doing. It's not defining an interval between two numbers, it's attempting to set the value of the first argument to the second, but in a way that doesn't make a lick of sense.
There are three problems (technically, four):
the compiler will yak on 0++ - a constant cannot be the operand of the ++ or -- operators;
If either i or limit are expressions, such as MAX(i+1, i+5) you're going to have the same problem with the ++ operator and you're going to have precedence issues;
assuming you fix those problems, you still have an infinite loop;
The (technical) fourth problem is ... using a macro as a function. I know, this is embedded world, and embedded world wants to minimize function call overhead. That's what the inline function specifier is supposed to buy you so you don't have to go through this heartburn.
But, okay, maybe the compiler available for the system you're working on doesn't support inline so you have to go through this exercise.
But you're going to have to go to the person who gave you this code and politely and respectfully ask, "what is this crap?"
cpp_magic extends what can typically be done with the C Preprocessor.
(It's a single header file and is on GitHub, here.)
The IF_ELSE(cond)(<true_result>, <false_result>) is a super useful macro!
How can expressions be evaluated in the cond clause?
It doesn't appear to work as advertised: with expressions in the cond part.
The following returns 10:
int greater = IF_ELSE(10 > 20)(10, 20);
The macro always returns the first argument, unless it is a simple 0 or 1.
Is the c argument (condition) a misnomer (and is really ust a simple value)?
I also tried this, according to a suggestion below, but it gives the same result:
#define GREATER(x,y) BOOL(x > y)
int greater = IF_ELSE(GREATER(10,20))(10, 20);
But it also evaluates to 10.
(Note that IF_ELSE already calls BOOL(c) on its argument.)
Has anyone used IF_ELSE with a general preprocessor expression?
Looking at cpp_magic, it looks a bit basic. If you want to evaluate math in the preprocessor using that, you have to basically implement the math yourself using macros. First off the bat, cpp_magic's IF_ELSE macro is defined as follows:
#define IF_ELSE(condition) _IF_ ## condition
That's a dead stop to using it as you prescribe, because this macro's expansion list contains a paste. The way macros expand involves four steps:
Argument substitution (a.s.; 6.10.3.1), where for each mention of a parameter in the macro's replacement list where said parameter is not participating in a paste or stringification, the corresponding argument is fully expanded, and the resulting expansion replaces the mention in the replacement list.
Stringification (6.10.3.2)
Pasting (6.10.3.3)
Rescan-and-further-replacement (r.a.s.r.; 6.10.3.4), where the resulting replacement list is rescanned; during this rescan the macro in question is marked as invalid for further expansion ("painted blue") to avoid recursion.
So in cpp_magic's implementation of IF_ELSE, no matter what you pass in as the condition, it will not do a.s.; instead, it will simply paste to _IF_. E.g., if you call IF_ELSE(BOOL(x)), you would simply get _IF_BOOL(x). You can patch this (but it's ugly, and there's a much much better library... see below) by adding an indirection macro like this:
#define EVAL_IF_ELSE(condition) IF_ELSE(condition)
...so you need at least this. For a greater comparision, you would need to implement greater. Here's a trivial implementation:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
// repeats X N times
#define N_TIMES(N,X) GLUE(N_TIMES_,N)(X)
#define N_TIMES_1(X) X
#define N_TIMES_2(X) X,N_TIMES_1(X)
#define N_TIMES_3(X) X,N_TIMES_2(X)
#define N_TIMES_4(X) X,N_TIMES_3(X)
#define N_TIMES_5(X) X,N_TIMES_4(X)
// pop; technically non-compliant for one parameter
// which I could code around, but this is a simplified
// demo only (and there's a much better way)
#define POP(...) POP_I(__VA_ARGS__)
#define POP_I(X,...) __VA_ARGS__
#define NTH(N,...) GLUE(NTH_,N)(__VA_ARGS__)
#define NTH_1(...) NTH_1_I(__VA_ARGS__,)
#define NTH_1_I(X,...) X
#define NTH_2(X,...) NTH_1(__VA_ARGS__)
#define NTH_3(X,...) NTH_2(__VA_ARGS__)
#define NTH_4(X,...) NTH_3(__VA_ARGS__)
#define NTH_5(X,...) NTH_4(__VA_ARGS__)
#define COMPARE(X,Y) NTH(X,POP(N_TIMES(Y,L)),E,N_TIMES(5,G))
#define GREATER(X,Y) GLUE(GREATER_RESULT_,COMPARE(X,Y))
#define GREATER_RESULT_L 0
#define GREATER_RESULT_E 0
#define GREATER_RESULT_G 1
...so that's a nice start. And this greater works perfectly... for numbers up to 5... so long as you ignore the 1 case. There's a skeleton here for how to do other comparisons, but they would only work up to 5. A demo working up to 20 is shown here.
This shows what you want to do is possible, but it's still a lot of work. Here I'm only showing a way to do a comparison; but everything else you want to do (add, sub, mul, div, etc) also needs an implementation, and each piece is code. If you want to play with it, knock yourself out, but I would recommend for play ditching the C language and just use your preprocessor like I do in the demo.
There is a much, much better way
...and that is to let someone else do all of the work for you. And they have! What you're in effect trying to do has been pulled into the boost preprocessor library. BPP also has add, sub, mul, div, and so on. For BPP's implementation, the saturation is at 255. Here's how you would do your conditional using boost preprocessor:
#include <boost/preprocessor/comparison.hpp>
#include <boost/preprocessor/control.hpp>
BOOST_PP_IF(BOOST_PP_GREATER(10,20),10,20)
...and a demo
I'm using the variadic macro to simulate a default argument. I compile with -Wunused-value. Thus, I get the following warning:
warning: left-hand operand of comma expression has no effect
Is there a way to somehow fix this warning without having to remove -Wunused-value? or do I have to end up using #pragma GCC diagnostic ignored "-Wunused-value"?
#include <stdio.h>
#define SUM(a,...) sum( a, (5, ##__VA_ARGS__) )
int sum (int a, int b)
{
return a + b;
}
int main()
{
printf("%d\n", SUM( 3, 7 ) );
printf("%d\n", SUM( 3 ) );
}
The ## construct that you are using is a gcc speciality and not portable. Don't use it, there are other ways.
The following should do what you expect
#define SUM2(A, B, ...) sum((A), (B))
#define SUM1(...) SUM2(__VA_ARGS__)
#define SUM(...) SUM1(__VA_ARGS__, 5, 0)
Such games with macro default arguments are frowned upon by many, because they may make the code more difficult to read.
For the moment I'd suggest that you don't use such constructs in your programs. You should perhaps learn more of the basics before you go into such esoteric stuff.
Also your idea to want to silence the compiler is really a bad one. The compiler is there to help you, listen to him. In the contrary, raise the warning level to a maximum and improve your code until it compiles without any warning.
Jens Gustedt proposed a very good problem-specific portable solution. I didn't know, ,##__VA_ARGS__ is a GCC extension (maybe Clang too?). There are however GCC-specific solutions for the authors initial intension.
As a problem-specific and very GCC-specific solution, you can use _Pragma("GCC diagnostic ignored \"-Wunused-value\"") and delimit it around the macro expansion. This will keep the comfort of readability. This does not work everywhere. It mainly fails within static initializer lists placed outside of functions where those pragmas can't be applied. I really was looking for a solution within such initializer lists because I couldn't find any which hides the warning pragmas from the reader. Other than that, for a function call like sum() for example - which I suppose to be only valid in a function body itself -, you can use it:
#define SUM(a,...) ({\
_Pragma("GCC diagnostic push")\
_Pragma("GCC diagnostic ignored \"-Wunused-value\"")\
sum( a, (5, ##__VA_ARGS__) );\
_Pragma("GCC diagnostic pop")\
})
Remember, you can only use it in function bodies and where an expression is expected. The warning will remain turned on after the macro expansion.
But I found a general solution! Conditional-macro-expansion is possible with the ,##__VA_ARGS__ feature. It gives you the power of conditional expansion based on blankness of the argument.
This feature does not necessarily add substitution power to the preprocessor. If you use arguments which include commas like (<...>) for false or 0 and (<...>,<...>) for true or 1, you can achieve the same. But only the conditional comma allows you the comfort of expanding conditionally based on the blankness of an argument.
See: you might be able to write your code like SUM(A) expanding to sum((A),5) without ##__VA_ARGS__ but you might not be able to write SUM(,B) expanding to sum((somevalue),B) . But you can do that with ##__VA_ARGS__ .
Example:
#define _VADIC(...) , ##__VA_ARGS__
//expands to A if A is not blank else to __VA_ARGS__ as fallback value
#define TRY(A,B) _TRY0(_VADIC(A), B)
#define _TRY0(...) _TRY1(__VA_ARGS__) /*expand before call*/
#define _TRY1(A, B, ...) B
//expands to THEN if A is blank otherwise expands to blank
#define IF(A,THEN) _IF0(_VADIC(A),THEN)
#define _IF0(...) _IF1(__VA_ARGS__) /*expand before call*/
#define _IF1(A,B,...) __VA_ARGS__
//expands to ELSE if A is not blank otherwise expands to blank
#define IFNOT(A,ELSE) _IFNOT0(_VADIC(A),,ELSE)
#define _IFNOT0(...) _IFNOT1(__VA_ARGS__) /*expand before call*/
#define _IFNOT1(A,B,C,...) C
#define IF_ELSE(A, THEN, ELSE) IF(A,THEN)IFNOT(A,ELSE)
Without the conditional comma, you only can expand conditionally on the number of arguments or on predefined concatenations but this way, you can use ANY single undefined symbol as condition.
PS: What about loops? Macros in C are designed to be finite for faster compilation. You won't get infinite loops since the limit of loop cycles depends on the source code size. Limited loops is the only thing which hinders you from turing-completeness, but practical real-world computer science problems (different from embedded or operating systems) don't need infinite loops for calculations. They are all limited depending with the problem size. The turing machine also uses a finite alphabet of symbols. You could know the limit of loop cycles which are needed in the worst case and it is possible to create a functional loop (a "reduce" or "filter" macro) running on variable-length macro argument lists which can reformat the macro argument list and do magic. The only requirement is the comma. You can't iterate over elements without a comma in between.
I was going through a code given to me by my teacher and I came across this:
#define setBit(var, bitnum) (var)|=(1<<(bitnum))
#define resBit(var, bitnum) (var)&=~(1<<(bitnum))
#define ROW_RESET setBit(PORTA,4) ; resBit(PORTA,4)
The first two #define statement are self explanatory but I am having problem understanding the third one. Is the third statement (line) right? Can we write statements after semi-colon(;), if yes then please can you explain what is happening here.
Semicolons may be used in macro definitions, and will simply have the effect of making the macro expand to multiple statements:
ROW_RESET;
will expand to
setBit(PORTA,4) ; resBit(PORTA,4);
It is also allowable to use e.g. block statements with curly braces in macro definitions, as long as the result after expansion is syntactically valid (at the point where it is expanded).
As usual, the more "fancy" stuff you do in a macro, the more easy it is to get it wrong - for example, if you have a function with the signature void foo(int), you could call foo(setBit(PORTA, 4)), but foo(ROW_RESET) would not compile, as it would expand to foo(setBit(PORTA,4) ; resBit(PORTA,4)) (which is illegal because you can't have semicolons inside a function call).
A more experienced C programmer would make them like this:
#define setBit(var, bitnum) ((var)|=(1<<(bitnum)))
#define resBit(var, bitnum) ((var)&=~(1<<(bitnum)))
#define ROW_RESET do { setBit(PORTA,4); resBit(PORTA,4); } while (0)
...and then, I'd go check that PORTA was defined as a simple constant and not some expression with possible side effects.
As for what the macro does, it's quite simple: it sets a bit to one, then sets the same bit to zero. This is probably an embedded system like an Atmel chip with some memory-mapped I/O at that address connected to something that triggers on a rising edge, and can handle a pulse smaller than the clock rate of the processor.
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
What’s the use of do while(0) when we define a macro?
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
C multi-line macro: do/while(0) vs scope block
I have seen a lot of usages like this, previously I though that the programmer wanted to break out of a block of code easily. Why do we need a do { ... } while (0) loop here? Are we trying to tell the compiler something?
For instance in Linux kernel 2.6.25, include/asm-ia64/system.h
/*
* - clearing psr.i is implicitly serialized (visible by next insn)
* - setting psr.i requires data serialization
* - we need a stop-bit before reading PSR because we sometimes
* write a floating-point register right before reading the PSR
* and that writes to PSR.mfl
*/
#define __local_irq_save(x) \
do { \
ia64_stop(); \
(x) = ia64_getreg(_IA64_REG_PSR); \
ia64_stop(); \
ia64_rsm(IA64_PSR_I); \
} while (0)
It's always used in macros so that a semicolon is required after a call, just like when calling a regular function.
In your example, you have to write
__local_irq_save(1);
while
__local_irq_save(1)
would result in an error about a missing semicolon. This would not happen if the do while was not there. If it was just about scoping, a simple curly brace pair would suffice.
It allows for the code to appear here:
if(a) __local_irq_save(x); else ...;
// -> if(a) do { .. } while(0); else ...;
If they simply used a { .. } you would get
if(a) { ... }; else ...;
The else would not belong to any if anymore, because the semicolon would be the next statement and separate the else from the preceeding if. A compile error would occur.
The purpose of do{ ... } while(0) construct is to turn a group of statements into a single compound statement that can be terminated with a ;. You see, in C language the do/while construct has one weird and unusual property: even though it "works" as a compound statement, it expects a ; at the end. No other compound constructs in C have this property.
Because of this property, you can use do/while to write multi-statement macros, which can be safely used as "ordinary" functions without worrying what's inside the macro, as in the following example
if (/* some condition */)
__local_irq_save(x); /* <- we can safely put `;` here */
else
/* whatever */;
The answer has already been given (so the macro forces a ; when called), but another use of this kind of statement that I have seen: it allows break to be called anywhere in the "loop", early terminating if needed. Essentially a "goto" that your fellow programmers wouldn't murder you for.
do {
int i = do_something();
if(i == 0) { break; } // Skips the remainder of the logic
do_something_else();
} while(0);
Note that this is still fairly confusing, so I don't encourage its use.
Looks like it's there just for scoping. It's similar to:
if (true)
{
// Do stuff.
}
edit
I don't see it in your example, but it's possible that one of those function calls is actually a macro, in which case there's one key difference between do/while(0) and if(true), which is that the former allows continue and break.
It makes use of the macro act like a real statement or function call.
A statement is either { expression-list } or expression; so that poses a problem when defining macros that need more than one expression, because if you use { } then a syntax error will occur if the caller of the macro quite reasonably adds a ; before an else.
if(whatever)
f(x);
else
f(y);
If f() is a single statement macro, fine, but what if it's a macro and something complicated? You end up with if(...) { s1; s2; }; else ... and that doesn't work.
So the writer of the macro has to then either make it into a real function, wrap the construct in a single statement, or use a gnu extension.
The do .. while(0) pattern is the "wrap the construct" approach.