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
Related
There were several questions here regarding variadic macros in C. These include:
How to make a variadic macro (variable number of arguments) which explains the basics, e.g., passing a variable number of arguments to functions such as printf
Is it possible to iterate over arguments in variadic macros?, which explains how to iteratively apply a macro to each of the arguments of the variadic macro.
https://github.com/swansontec/map-macro which explains how to do so on pairs
My question is related to the iteration technique. I am interested in a macro with this generalized semantics.
ITERATE(Before, Action, Between, After, Empty, ...)
that will place Before prior to all expansions, apply Action to each argument, place Between between every two consecutive applications, and will finally place the expansion of After. Moreover, if the number of argument With such a macro, it should be possible to write
// Loop elements
#define A(x) (x)
#define Befor (
#define After )
#define Between ||
#define Empty 1
// Define an OR macro
#define OR(...) ITERATE(Before, A, Between, Empty, __VA_ARGS__)
// Use it
OR() // Expands to 1
OR(a) // Expands to ((a))
OR(a,b) // Expands to ((a)||(b))
OR(a,b,c) // Expands to to ((a)||(b)||(c))
The purpose of course is not to write an OR function. A generalized functionality could be for more intricate applications. E.g., a macro for defining classes and functions, something to print the trace, etc.
I never liked the recursive REPEAT() macro idiom - it generates horrible hour long read error messages that are.. recursive, so you don't know where the error is and it's also hard to grasp how the OBSTRUCT(REPEAT_INDIRECT) () stuff works. Overall, overloading the macro on number of arguments and using an external tool (shell script or m4 preprocessor) to generate C source code is waay easier, easier to read, maintain and fix and also you can expand the macros on the tools side removing the burden of recursive expansion on C side. With that in mind, your ITERATE can be generated with existing preprocessor libraries, P99_FOR or BOOST_FOREACH comes to mind.
Also, typing shift all the time is strange - I prefer snake case. Here's a reduced example without Before and After macros with overloading the macro on number of arguments:
#define _in_ITERATE_0(f,b,e) e()
#define _in_ITERATE_1(f,b,e,_1) f(_1)
#define _in_ITERATE_2(f,b,e,_1,...) f(_1)b()_in_ITERATE_1(f,b,e,__VA_ARGS__)
#define _in_ITERATE_3(f,b,e,_1,...) f(_1)b()_in_ITERATE_2(f,b,e,__VA_ARGS__)
// or you could expand it instead of reusing previous one with same result:
#define _in_ITERATE_4(f,b,e,_1,_2,_3,_4) f(_1)b()f(_2)b()f(_3)b()f(_4)
// etc.... generate
#define _in_ITERATE_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) _in_ITERATE_##N
#define ITERATE(func, between, empty, ...) \
_in_ITERATE_N(0,##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)(func, between, empty, ##__VA_ARGS__)
#define _in_OR_OP(x) (x)
#define _in_OR_EMPTY() 1
#define _in_OR_BETWEEN() ||
#define OR(...) (ITERATE(_in_OR_OP, _in_OR_BETWEEN, _in_OR_EMPTY, ##__VA_ARGS__))
// Use it
OR() // Expands to (1)
OR(a) // Expands to ((a))
OR(a,b) // Expands to ((a)||(b))
OR(a,b,c) // Expands to to ((a)||(b)||(c))
outputs:
(1)
((a))
((a)||(b))
((a)||(b)||(c))
For more examples on overloading macro on count of arguments see this thread. I am using ## GNU extension to remove the comma before __VA_ARGS__ because I am used to using it - I think __VA_OPT__(,) should be nowadays preferred, I am not sure.
This is not my exact issue, but an example is as follows
// Method 1
#define Function(argument) (StupidLongArray[argument] + OtherStupidLongArrayName[argument] * WhyAreMyNamesSoLong[argument])
or, prefered
// Method 2
#define _SLA StupidLongArray
#define _OSLAN OtherStupidLongArrayName
#define _WAMNSL WhyAreMyNamesSoLong
#define Function(argument) (_SLA[argument] + _OSLAN[argument] * _WAMNSL[argument])
#undef _SLA
...
...
My understanding of these preprocessor directions implies that once Function has been defined, I can get rid of _SLA and friends. This is not the case.
How come? Is their a way around it?
Unfortunately, there's no way around this. Preprocessor processing basically involves substituting the macro into the original line, then reprocessing it again. So if you call Function(x), it becomes (_SLA[x] + _OSLAN[x] * _WAMNSL[x]) first, then the _SLA, _OSLAN, and _WAMNSL macros are substituted afterward. If they're not defined at the place where this substitution occurs, they'll be left as is.
In addition to the posted answer, you could consider rewriting the macro as:
#define Function(argument) \
( StupidLongArray[argument] \
+ OtherStupidLongArrayName[argument] \
* WhyAreMyNamesSoLong[argument] )
This much clearer and easier to read than your version with short, nonsense abbreviations.
Might also be wise to swap the order of appearance so that the multiplication operation is placed before the addition, to make the code easier to read.
As a side note, you should avoid giving identifiers names that start with _ since that might collide with library identifiers.
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.
How are the definitions in C processed? Are they processed in order of line numbers?
For example, will the following statements work?
#define ONE 1
#define TWO (ONE+1)
Could there be any problems with definitions that depend on previous definitions?
Yes, one #define can reference other #define substitutions and macros without any problem.
Moreover, the expression on these constants would remain a constant expression.
Your second expression would be textually equivalent to (ONE+1) replacement in the text, with no limits to the level of nesting. In other words, if you later define
#define THREE (TWO+1)
and then use it in an assignment i = THREE, you would get
i = ((ONE+1)+1)
after preprocessing.
If you are planning to use this trick with numeric values, a common alternative would be to use an enum with specific values, i.e.
enum {
ONE = 1
, TWO = ONE+1
, THREE = TWO+1
, ... // and so on
};
They're processed at point when they're used, so you example and even this
#define TWO (ONE+1)
#define ONE 1
will work.
The best way is to check by yourself:
g++ test.cpp
gcc test.c
For strict compiler check:
gcc test.c -pedantic
And all worked for me!
test.c/test.cpp
#include <stdio.h>
#define A 9
#define B A
int main()
{
printf("%d\n",B);
return 0;
}
The compiler processes the #define-s in the order they were de...fined. After each #define gets processed, the preprocessor then proceeds to process all text after this #define, using it in the state left by this #define. So, in your example:
#define ONE 1
#define TWO (ONE+1)
It first processes #define ONE 1, replacing all further occurunces of ONE with 1. So, the second macro becomes
#define TWO (1+1)
That is how it will be processed and applied by the preprocessor.
The reverse example:
#define TWO (ONE+1)
#define ONE 1
will also work. Why? Well, the preprocessor will take the first #define, scan the code for any occurences of TWO, and replace it with (ONE+1). Then it reaches the second #define, and replaces all occurences of ONE, including those put in place by the previous #define, with 1.
I'd personally prefer the former approach over the latter: it's plainly easier for the preprocessor to handle.