Related
Suppose I have function bshow() with signature
void bshow(int arg0, int arg1, int arg2);
but for arbitrary reasons I want to implement it as a macro.
Furthermore, I want the function have default arguments
int arg0=0x10;
int arg1=0x11;
int arg2=0x12;
I've already done this for the case that bshow() is a function, using the standard tricks.
But how can I do it as a macro?
Eg. suppose I have a macro nargs() that uses the C Preprocessor to count the number of arguments. Eg.
nargs() // get replaced by 0 by the preprocessor
nargs(a) // get replaced by 1 by the preprocessor
nargs(a,b) // get replaced by 2 by the preprocessor
I'd like to do something like (which doesn't work):
#define arg0_get(a0,...) a0
#define arg1_get(a0,a1,...) a1
#define arg2_get(a0,a1,a2,...) a2
#define bshow(...) do{ \
int arg0=0x10; if(0<nargs(__VA_ARGS__)) arg0 = arg0_get(__VA_ARGS__); \
int arg1=0x11; if(1<nargs(__VA_ARGS__)) arg1 = arg1_get(__VA_ARGS__); \
int arg2=0x12; if(2<nargs(__VA_ARGS__)) arg2 = arg2_get(__VA_ARGS__); \
/* do stuff here */ \
}while(0)
Actually I've already implemented the bshow() function as a macro, as follows (here it has the actual number of arguments):
#define __bshow(bdim,data, nbits,ncols,base)({ \
bdim,data, nbits,ncols,base; \
putchar(0x0a); \
printf("nbits %d\n",nbits); \
printf("ncols %d\n",ncols); \
printf("base %d\n",base); \
})
#define _bshow(bdim,data, nbits,ncols,base, ...) __bshow(bdim,data, nbits,ncols,base)
#define bshow(...) \
if( 2==nargs(__VA_ARGS__)) _bshow(__VA_ARGS__, 32,24,16,0,__VA_ARGS__); \
else if(3==nargs(__VA_ARGS__)) _bshow(__VA_ARGS__, 24,16,0,__VA_ARGS__); \
else if(4==nargs(__VA_ARGS__)) _bshow(__VA_ARGS__, 16,0,__VA_ARGS__); \
else if(5==nargs(__VA_ARGS__)) _bshow(__VA_ARGS__, 0,__VA_ARGS__); \
// test
bshow(0,1);
bshow(0,1, 10);
bshow(0,1, 10,11);
bshow(0,1, 10,11,12);
EDIT:
The proposed solution doesn't have the intended effect because it seems to "instantiate" all instances of the macro, which in general has unintended consequences.
But I wonder if there's a more elegant way to do it.
It'd also be nice to abstract away the entire construction inside its own macro, so that one can apply it to other functions easily, as opposed to having to write the boilerplate manually for each function/macro.
Also this wasn't too helpful.
I found a nice answer.
What you do is you call the vfn() macro, which is (I think) a higher-order macro that returns a macro that returns the token concatenated with the number of args (in hex base, no 0-padding) and then evaluates it at the args. Or something.
Eg. supposed you want to overload a macro called bshow(). You #define the macro bshow() as #define bshow() vfn(bshow,__VA_ARGS__), and you define 1 instance of bshow for each argument count (eg. #define bshow0(...), for 0 arguments, #define bshow1(...) for 1 argument, #define bshow2(...) for 2 arguments, etc.). So now, eg., bshow(0,1) returns bshow2() (because you called it with 2 arguments) evaluated at (0,1), which is _bshow(0,1, 16,32,16), and then _bshow(0,1, 16,32,16) gets evaluated too. You can check the final preprocessor output by running gcc with the -E option, but the intermediate steps are hard to understand (for me).
You also need to decide on the mandatory args and the optional args.
That's almost all I (sort of) understand about what's going on, although I did upload a YT tutorial a while ago on how the argument-counting works.
// ----------------------------------------------------------------
// library
#define __nargs100__(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a0a,a0b,a0c,a0d,a0e,a0f,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a1a,a1b,a1c,a1d,a1e,a1f,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a2a,a2b,a2c,a2d,a2e,a2f,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a3a,a3b,a3c,a3d,a3e,a3f,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a4a,a4b,a4c,a4d,a4e,a4f,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a5a,a5b,a5c,a5d,a5e,a5f,a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a6a,a6b,a6c,a6d,a6e,a6f,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a7a,a7b,a7c,a7d,a7e,a7f,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a8a,a8b,a8c,a8d,a8e,a8f,a90,a91,a92,a93,a94,a95,a96,a97,a98,a99,a9a,a9b,a9c,a9d,a9e,a9f,aa0,aa1,aa2,aa3,aa4,aa5,aa6,aa7,aa8,aa9,aaa,aab,aac,aad,aae,aaf,ab0,ab1,ab2,ab3,ab4,ab5,ab6,ab7,ab8,ab9,aba,abb,abc,abd,abe,abf,ac0,ac1,ac2,ac3,ac4,ac5,ac6,ac7,ac8,ac9,aca,acb,acc,acd,ace,acf,ad0,ad1,ad2,ad3,ad4,ad5,ad6,ad7,ad8,ad9,ada,adb,adc,add,ade,adf,ae0,ae1,ae2,ae3,ae4,ae5,ae6,ae7,ae8,ae9,aea,aeb,aec,aed,aee,aef,af0,af1,af2,af3,af4,af5,af6,af7,af8,af9,afa,afb,afc,afd,afe,aff,a100,...) a100
#define __nargs__(...) __nargs100__(,##__VA_ARGS__, ff,fe,fd,fc,fb,fa,f9,f8,f7,f6,f5,f4,f3,f2,f1,f0,ef,ee,ed,ec,eb,ea,e9,e8,e7,e6,e5,e4,e3,e2,e1,e0,df,de,dd,dc,db,da,d9,d8,d7,d6,d5,d4,d3,d2,d1,d0,cf,ce,cd,cc,cb,ca,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0,bf,be,bd,bc,bb,ba,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0,af,ae,ad,ac,ab,aa,a9,a8,a7,a6,a5,a4,a3,a2,a1,a0,9f,9e,9d,9c,9b,9a,99,98,97,96,95,94,93,92,91,90,8f,8e,8d,8c,8b,8a,89,88,87,86,85,84,83,82,81,80,7f,7e,7d,7c,7b,7a,79,78,77,76,75,74,73,72,71,70,6f,6e,6d,6c,6b,6a,69,68,67,66,65,64,63,62,61,60,5f,5e,5d,5c,5b,5a,59,58,57,56,55,54,53,52,51,50,4f,4e,4d,4c,4b,4a,49,48,47,46,45,44,43,42,41,40,3f,3e,3d,3c,3b,3a,39,38,37,36,35,34,33,32,31,30,2f,2e,2d,2c,2b,2a,29,28,27,26,25,24,23,22,21,20,1f,1e,1d,1c,1b,1a,19,18,17,16,15,14,13,12,11,10,f,e,d,c,b,a,9,8,7,6,5,4,3,2,1,0)
#define __vfn(name, n) name##n
#define _vfn( name, n) __vfn(name, n)
#define vfn( fn, ...) _vfn(fn, __nargs__(__VA_ARGS__))(__VA_ARGS__)
// ----------------------------------------------------------------
// example
// backend: actual implementation, 2 mandatory args, 3 optional args
#define _bshow(bdim,data, ncols,nbits,base)({ \
/* do stuff here */ \
})
// "frontend", default arguments get implemented here. the suffix is the number of arguments, in hexadecimal base
#define bshow2(...) _bshow(__VA_ARGS__, 16,32,16)
#define bshow3(...) _bshow(__VA_ARGS__, 32,16)
#define bshow4(...) _bshow(__VA_ARGS__, 16)
#define bshow5(...) _bshow(__VA_ARGS__)
#define bshow(...) vfn(bshow,__VA_ARGS__)
// test
bshow(0x100,data0);
bshow(0x100,data0, 14);
bshow(0x100,data0, 12,16);
bshow(0x100,data0, 10, 8,2);
Let's assume I have a macro (more details about why, is below in the P.S. section)
void my_macro_impl(uint32_t arg0, uint32_t arg1, uint32_t arg2);
...
#define MY_MACRO(arg0, arg1, arg2) my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2))
The HW on which this macro is going to be used is little endian and uses 32bit architecture so that all the pointers are up to (and including) 32 bit width. My goal is to warn the user when it passes uint64_t or int64_t parameter by mistake.
I was thinking about using sizeof like this
#define MY_MACRO(arg0, arg1, arg2) do \
{ \
static_assert(sizeof(arg0) <= sizeof(uint32_t)); \
static_assert(sizeof(arg1) <= sizeof(uint32_t)); \
static_assert(sizeof(arg2) <= sizeof(uint32_t)); \
my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2)); \
} while (0)
But the user can use MY_MACRO with a bit-field and then my code fails to compile:
error: invalid application of 'sizeof' to bit-field
Question: Is there an option to detect at the compilation time if the size of the macro argument larger than, let's say, uint32_t?
P.S.
The MY_MACRO is going to act similarly to printf in a real-time embedded environment. This environment has a HW logger which may receive up to 5 parameters, each parameter should be 32 bits. The goal is to preserve the standard format as for printf. The format strings are parsed offline and the parser is well aware that every parameter is 32 bits, so it will cast it based on the %... from the format string. Possible usages are below.
Not desired usage:
uint64_t time = systime_get();
MY_MACRO_2("Starting execution at systime %llx", time); // WRONG! only the low 32 bits are printed. I want to detect it and fail the compilation.
Expected usage:
uint64_t time = systime_get();
MY_MACRO_3("Starting execution at systime %x%x", (uint32_t)(time >> 32), (uint32_t)time); // OK!
The following approach may work for this need:
#define CHECK_ARG(arg) _Generic((arg), \
int64_t : (arg), \
uint64_t : (arg), \
default : (uint32_t)(arg))
Then, the MY_MACRO can be defined as
#define MY_MACRO(a0, a1, a2) do \
{ \
uint32_t arg1 = CHECK_ARG(a0); \
uint32_t arg2 = CHECK_ARG(a1); \
uint32_t arg3 = CHECK_ARG(a2); \
my_macro_impl(arg1, arg2, arg3);\
} while (0)
In such case, when passing for example uint64_t, a warning is fired:
warning: implicit conversion loses integer precision: 'uint64_t' (aka
'unsigned long long') to 'uint32_t' (aka 'unsigned int')
[-Wshorten-64-to-32]
Note:
Other types like double, 128/256 bit types can be handled similarly.
Appropriate warnings should be enabled.
EDIT:
Inspired by Lundin's comment and answer, the proposed above solution can easily be modified to a portable version which will cause compilation error and not just a compiler warning.
#define CHECK_ARG(arg) _Generic((arg), \
int64_t : 0, \
uint64_t : 0, \
default : 1)
So the MY_MACRO can be modified to
#define MY_MACRO(a0, a1, a2) do \
{ \
_Static_assert(CHECK_ARG(a1) && \
CHECK_ARG(a2) && \
CHECK_ARG(a3), \
"64 bit parameters are not supported!"); \
my_macro_impl((uint32_t)(a1), (uint32_t)(a2), (uint32_t)(a3)); \
} while (0)
This time, when passing uint64_t parameter MY_MACRO(1ULL, 0, -1), the compilation fails with error:
error: static_assert failed due to requirement '_Generic((1ULL), long
long: 0, unsigned long long: 0, default: 1) && (_Generic((0), long
long: 0, unsigned long long: 0, default:
1) && _Generic((-1), long long: 0, unsigned long long: 0, default: 1))' "64 bit parameters are not supported!"
The type of the ternary ?: expression is the common type of its second and third arguments (with integer promotion of smaller types). So the following version of your MY_MACRO will work in a 32-bit architecture:
static_assert(sizeof(uint32_t) == sizeof 0, ""); // sanity check, for your machine
#define MY_MACRO(arg0, arg1, arg2) \
do { \
static_assert(sizeof(0 ? 0 : (arg0)) == sizeof 0, ""); \
static_assert(sizeof(0 ? 0 : (arg1)) == sizeof 0, ""); \
static_assert(sizeof(0 ? 0 : (arg2)) == sizeof 0, ""); \
my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2)); \
} while (0)
Moreover, this solution should work with all versions of C and C++ (with, if necessary, a suitable definition of static_assert).
Note this macro, like the OP's original, has function semantics in that the arguments are evaluated only once, unlike for example the notorious MAX macro.
Question: Is there an option to detect at the compilation time if the size of the macro argument larger than, let's say, uint32_t?
The only way to do this portably, is by generating a compiler error with _Generic. If you want the error to be pretty and readable, you feed the result of _Generic to _Static_assert, so that you can type out a custom string as compiler message.
Your specification seems to be this:
Everything must be compile-time checks.
The macro can get 1 to 5 parameters of any type.
Only int32_t and uint32_t are allowed types.
This means that you have to write a variadic macro and it must accept 1 to 5 parameters.
Such a macro can be written like this:
#define COUNT_ARGS(...) ( sizeof((uint32_t[]){__VA_ARGS__}) / sizeof(uint32_t) )
#define MY_MACRO(...) \
_Static_assert(COUNT_ARGS(__VA_ARGS__)>0 && COUNT_ARGS(__VA_ARGS__)<=5, \
"MY_MACRO: Wrong number of arguments");
COUNT_ARGS creates a temporary compound literal of as many objects as you give the macro. If they are wildly incompatible with uint32_t you might get compiler errors/warnings here already. If not, COUNT_ARGS will return the number of arguments passed.
With that out of the way, we can do the actual, portable type check of each item in the variable argument list. To check the type of one single item with _Generic:
#define CHECK(arg) _Generic((arg), uint32_t: 1, int32_t: 1, default: 0)
Then pass the result of that on to _Static_assert. However, for 5 arguments we would need to check 1 to 5 items. We can "chain" a number of macros for this purpose:
#define CHECK(arg) _Generic((arg), uint32_t: 1, int32_t: 1, default: 0)
#define CHECK_ARGS1(arg1,...) CHECK(arg1)
#define CHECK_ARGS2(arg2,...) (CHECK(arg2) && CHECK_ARGS1(__VA_ARGS__,0))
#define CHECK_ARGS3(arg3,...) (CHECK(arg3) && CHECK_ARGS2(__VA_ARGS__,0))
#define CHECK_ARGS4(arg4,...) (CHECK(arg4) && CHECK_ARGS3(__VA_ARGS__,0))
#define CHECK_ARGS5(arg5,...) (CHECK(arg5) && CHECK_ARGS4(__VA_ARGS__,0))
Each macro checks the first argument passed to it, then forwards the rest of them, if any, to the next macro. The trailing 0 is there to shut up ISO C warnings about rest arguments required for variadic macros.
We can bake the calls to these into a _Static_assert that calls the proper macro in the "chain" corresponding to the number of arguments:
_Static_assert(COUNT_ARGS(__VA_ARGS__) == 1 ? CHECK_ARGS1(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 2 ? CHECK_ARGS2(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 3 ? CHECK_ARGS3(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 4 ? CHECK_ARGS4(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 5 ? CHECK_ARGS5(__VA_ARGS__,0) : 0, \
"MY_MACRO: incorrect type in parameter list " #__VA_ARGS__); \
Full code with examples of use:
#include <stdint.h>
#define COUNT_ARGS(...) ( sizeof((uint32_t[]){__VA_ARGS__}) / sizeof(uint32_t) )
#define CHECK(arg) _Generic((arg), uint32_t: 1, int32_t: 1, default: 0)
#define CHECK_ARGS1(arg1,...) CHECK(arg1)
#define CHECK_ARGS2(arg2,...) (CHECK(arg2) && CHECK_ARGS1(__VA_ARGS__,0))
#define CHECK_ARGS3(arg3,...) (CHECK(arg3) && CHECK_ARGS2(__VA_ARGS__,0))
#define CHECK_ARGS4(arg4,...) (CHECK(arg4) && CHECK_ARGS3(__VA_ARGS__,0))
#define CHECK_ARGS5(arg5,...) (CHECK(arg5) && CHECK_ARGS4(__VA_ARGS__,0))
#define MY_MACRO(...) \
do { \
_Static_assert(COUNT_ARGS(__VA_ARGS__)>0 && COUNT_ARGS(__VA_ARGS__)<=5, \
"MY_MACRO: Wrong number of arguments"); \
_Static_assert(COUNT_ARGS(__VA_ARGS__) == 1 ? CHECK_ARGS1(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 2 ? CHECK_ARGS2(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 3 ? CHECK_ARGS3(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 4 ? CHECK_ARGS4(__VA_ARGS__,0) : \
COUNT_ARGS(__VA_ARGS__) == 5 ? CHECK_ARGS5(__VA_ARGS__,0) : 0, \
"MY_MACRO: incorrect type in parameter list " #__VA_ARGS__); \
} while(0)
int main (void)
{
//MY_MACRO(); // won't compile, "empty initializer braces"
//MY_MACRO(1,2,3,4,5,6); // static assert "MY_MACRO: Wrong number of arguments"
MY_MACRO(1); // OK, all parameters int32_t or uint32_t
MY_MACRO(1,2,3,4,5); // OK, -"-
MY_MACRO(1,(uint32_t)2,3,4,5); // OK, -"-
//MY_MACRO(1,(uint64_t)2,3,4,5); // static assert "MY_MACRO: incorrect type..."
//MY_MACRO(1,(uint8_t)2,3,4,5); // static assert "MY_MACRO: incorrect type..."
}
This should be 100% portable and doesn't rely on the compiler giving extra diagnostics beyond what's required by the standard.
The old do-while(0) trick is there to allow compatibility with icky-style brace formatting standards such as if(x) MY_MACRO(1) else. See Why use apparently meaningless do-while and if-else statements in macros?
I'm having a project that does lots of this
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
// do some legacy stuff
#else
// do current stuff
#endif
where KERNEL_VERSION is defined as
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
I'd like to eliminate the defines that are not relevant for the current version, but tools like sunifdef don't evaluate the KERNEL_VERSION macro, so something like
sunifdef --replace -DKERNEL_VERSION\(a,b,c\)=\(\(\(a\)\<\<16\)+\(\(b\)\<\<8\)+\(c\)\) -DLINUX_VERSION_CODE=3.13.1 *
fails with the message
sunifdef: error 0x04200: Garbage in argument "-DKERNEL_VERSION(a,b,c)=(((a)<<16)+((b)<<8)+(c))"
How do I get around this?
With sunifdef 3.1.3, you can't do it, as you demonstrated. Nor can you do it with earlier versions of coan such as 4.2.2.
However, with coan 5.2 (the current version), you can almost do what you are after.
$ cat legacy.c
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
do(some,legacy,stuff)
#else
do(current,stuff)
#endif
$ coan source -DLINUX_VERSION_CODE=0x020635 legacy.c
coan: /Users/jleffler/soq/legacy.c: line 1: warning 0x0041c: "-DKERNEL_VERSION(a,b,c)=(((a) << 16) + ((b) << 8) + (c))" has been assumed for the current file
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
do(current,stuff)
$ coan source -DLINUX_VERSION_CODE=0x020624 legacy.c
coan: /Users/jleffler/soq/legacy.c: line 1: warning 0x0041c: "-DKERNEL_VERSION(a,b,c)=(((a) << 16) + ((b) << 8) + (c))" has been assumed for the current file
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
do(some,legacy,stuff)
$
This is close to what you want, but not quite. It gives 'correct' output, but maybe not 'helpful' output. It gives you the code that would be compiled for the LINUX_VERSION_CODE specified on the command line, whereas you'd probably like the conditionals based on LINUX_VERSION_CODE and KERNEL_VERSION that are not false to survive into the output.
The successor to sunifdef seems to be coan, and the following command seemed to work (on one simple file):
coan source "-DLINUX_VERSION=KERNEL_VERSION(2,18,1)" \
"-DKERNEL_VERSION(a,b,c)=((a)*0x10000 + (b)*0x100 + (c))" \
testfile.c
I think using the KERNEL_VERSION macro to define LINUX_VERSION is prettier, but you might prefer Chris Dodd's version in hex. Two dots in a number is definitely not going to work.
The error is coming from the fact that you can't define macros with arguments on the command line with -D -- you can only define simple macros. However, you shouldn't NEED to define KERNEL_VERSION on the command line as the #define in the source should be fine. You should only need -DLINUX_VERSION_CODE=0x30d01 -- you need to define it as a single integer constant (hex is easiest) rather than withs dots.
Is there some way of getting optional parameters with C++ Macros? Some sort of overloading would be nice too.
Here's one way to do it. It uses the list of arguments twice, first to form the name of the helper macro, and then to pass the arguments to that helper macro. It uses a standard trick to count the number of arguments to a macro.
enum
{
plain = 0,
bold = 1,
italic = 2
};
void PrintString(const char* message, int size, int style)
{
}
#define PRINT_STRING_1_ARGS(message) PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size) PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)
#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )
#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);
return 0;
}
This makes it easier for the caller of the macro, but not the writer.
With great respect to Derek Ledbetter for his answer — and with apologies for reviving an old question.
Getting an understanding of what it was doing and picking up elsewhere on the ability to preceed the __VA_ARGS__ with ## allowed me to come up with a variation...
// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0() <code for no arguments>
#define XXX_1(A) <code for one argument>
#define XXX_2(A,B) <code for two arguments>
#define XXX_3(A,B,C) <code for three arguments>
#define XXX_4(A,B,C,D) <code for four arguments>
// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...) FUNC
// The macro that the programmer uses
#define XXX(...) XXX_X(,##__VA_ARGS__,\
XXX_4(__VA_ARGS__),\
XXX_3(__VA_ARGS__),\
XXX_2(__VA_ARGS__),\
XXX_1(__VA_ARGS__),\
XXX_0(__VA_ARGS__)\
)
For non-experts like me who stumble upon the answer, but can't quite see how it works, I'll step through the actual processing, starting with the following code...
XXX();
XXX(1);
XXX(1,2);
XXX(1,2,3);
XXX(1,2,3,4);
XXX(1,2,3,4,5); // Not actually valid, but included to show the process
Becomes...
XXX_X(, XXX_4(), XXX_3(), XXX_2(), XXX_1(), XXX_0() );
XXX_X(, 1, XXX_4(1), XXX_3(1), XXX_2(1), XXX_1(1), XXX_0(1) );
XXX_X(, 1, 2, XXX_4(1,2), XXX_3(1,2), XXX_2(1,2), XXX_1(1,2), XXX_0(1,2) );
XXX_X(, 1, 2, 3, XXX_4(1,2,3), XXX_3(1,2,3), XXX_2(1,2,3), XXX_1(1,2,3), XXX_0(1,2,3) );
XXX_X(, 1, 2, 3, 4, XXX_4(1,2,3,4), XXX_3(1,2,3,4), XXX_2(1,2,3,4), XXX_1(1,2,3,4), XXX_0(1,2,3,4) );
XXX_X(, 1, 2, 3, 4, 5, XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );
Which becomes just the sixth argument...
XXX_0();
XXX_1(1);
XXX_2(1,2);
XXX_3(1,2,3);
XXX_4(1,2,3,4);
5;
PS: Remove the #define for XXX_0 to get a compile error [ie: if a no-argument option is not allowed].
PPS: Would be nice to have the invalid situations (eg: 5) be something that gives a clearer compilation error to the programmer!
PPPS: I'm not an expert, so I'm very happy to hear comments (good, bad or other)!
With greatest respect to Derek Ledbetter, David Sorkovsky, Syphorlate for their answers, together with the ingenious method to detect empty macro arguments by Jens Gustedt at
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
finally I come out with something that incorporates all the tricks, so that the solution
Uses only standard C99 macros to achieve function overloading, no GCC/CLANG/MSVC extension involved (i.e., comma swallowing by the specific expression , ##__VA_ARGS__ for GCC/CLANG, and implicit swallowing by ##__VA_ARGS__ for MSVC). So feel free to pass the missing --std=c99 to your compiler if you wish =)
Works for zero argument, as well as unlimited number of arguments, if you expand it further to suit your needs
Works reasonably cross-platform, at least tested for
GNU/Linux + GCC (GCC 4.9.2 on CentOS 7.0 x86_64)
GNU/Linux + CLANG/LLVM, (CLANG/LLVM 3.5.0 on CentOS 7.0 x86_64)
OS X + Xcode, (XCode 6.1.1 on OS X Yosemite 10.10.1)
Windows + Visual Studio, (Visual Studio 2013 Update 4 on Windows 7 SP1 64 bits)
For the lazies, just skip to the very last of this post to copy the source. Below is the detailed explanation, which hopefully helps and inspires all people looking for the general __VA_ARGS__ solutions like me. =)
Here's how it goes. First define the user-visible overloaded "function", I named it create, and the related actual function definition realCreate, and the macro definitions with different number of arguments CREATE_2, CREATE_1, CREATE_0, as shown below:
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
The MACRO_CHOOSER(__VA_ARGS__) part ultimately resolves to the macro definition names, and the second (__VA_ARGS__) part comprises their parameter lists. So a user's call to create(10) resolves to CREATE_1(10), the CREATE_1 part comes from MACRO_CHOOSER(__VA_ARGS__), and the (10) part comes from the second (__VA_ARGS__).
The MACRO_CHOOSER uses the trick that, if __VA_ARGS__ is empty, the following expression is concatenated into a valid macro call by the preprocessor:
NO_ARG_EXPANDER __VA_ARGS__ () // simply shrinks to NO_ARG_EXPANDER()
Ingeniusly, we can define this resulting macro call as
#define NO_ARG_EXPANDER() ,,CREATE_0
Note the two commas, they are explained soon. The next useful macro is
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
so the calls of
create();
create(10);
create(20, 20);
are actually expanded to
CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);
As the macro name suggests, we are to count number of arguments later. Here comes another trick: the preprocessor only does simple text replacement. It infers the number of arguments of a macro call merely from the number of commas it sees inside the parentheses. The actual "arguments" separated by commas need not to be of valid syntax. They can be any text. That's to say, in the above example, NO_ARG_EXPANDER 10 () is counted as 1 argument for the middle call. NO_ARG_EXPANDER 20 and 20 () are counted as 2 arguments for the bottom call respectively.
If we use the following helper macros to further expand them
##define CHOOSE_FROM_ARG_COUNT(...) \
FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
FUNC_CHOOSER argsWithParentheses
The trailing , after CREATE_1 is a work-around for GCC/CLANG, suppressing a (false positive) error saying that ISO C99 requires rest arguments to be used when passing -pedantic to your compiler. The FUNC_RECOMPOSER is a work-around for MSVC, or it can not count number of arguments (i.e., commas) inside the parentheses of macro calls correctly. The results are further resolved to
FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);
As the eagle-eyed you may have seen, the last only step we need is to employ a standard argument counting trick to finally pick the wanted macro version names:
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
which resolves the results to
CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);
and certainly gives us the desired, actual function calls:
realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);
Putting all together, with some rearrangement of statements for better readability, the whole source of the 2-argument example is here:
#include <stdio.h>
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30); // Compilation error
return 0;
}
Although complicated, ugly, burdening the API developer, there comes a solution for overloading and setting optional parameters of C/C++ functions to us crazy people. The usage of the out-coming overloaded APIs become very enjoyable and pleasant. =)
If there is any further possible simplification of this approach, please do let me know at
https://github.com/jason-deng/C99FunctionOverload
Again special thanks to all of the brilliant people that inspired and led me to achieve this piece of work! =)
C++ macros haven't changed from C. Since C didn't have overloading and default arguments for functions, it certainly didn't have them for macros. So to answer your question: no, those features don't exist for macros. Your only option is to define multiple macros with different names (or not use macros at all).
As a sidenote: In C++ it's generally considered good practice to move away from macros as much as possible. If you need features like this, there's a good chance you're overusing macros.
For anyone painfully searching some VA_NARGS solution that works with Visual C++. Following macro worked for me flawlessly(also with zero parameters!) in visual c++ express 2010:
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...) bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0
If you want a macro with optional parameters you can do:
//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__)))
That worked for me aswell in vc. But it doesn't work for zero parameters.
int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
gcc/g++ supports varargs macros but I don't think this is standard, so use it at your own risk.
More concise version of Derek Ledbetter's code:
enum
{
plain = 0,
bold = 1,
italic = 2
};
void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}
#define PRINT_STRING(...) PrintString(__VA_ARGS__)
int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);
return 0;
}
#include <stdio.h>
#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b
#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
" actually means will be build in %s\n", (answer), (computer), (location))
int
main (int argc, char *argv[])
{
THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}
DISCLAIMER: Mostly harmless.
As a big fan of horrible macro monsters, I wanted to expand on Jason Deng's answer and make it actually usable. (For better or worse.) The original is not very nice to use because you need to modify the big alphabet soup every time you want to make a new macro and it's even worse if you need different amount of arguments.
So I made a version with these features:
0 argument case works
1 to 16 arguments without any modifications to the messy part
Easy to write more macro functions
Tested in gcc 10, clang 9, Visual Studio 2017
Currently I just made 16 argument maximum, but if you need more (really now? you're just getting silly...) you can edit FUNC_CHOOSER and CHOOSE_FROM_ARG_COUNT, then add some commas to NO_ARG_EXPANDER.
Please see Jason Deng's excellent answer for more details on the implementation, but I'll just put the code here:
#include <stdio.h>
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)
// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
do { \
/* put whatever code you want in the last macro */ \
realCreate(x, y); \
} while(0)
int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30); // Compilation error
return 0;
}
That's not really what the preprocessor is designed for.
That said, if you want to enter into the area of seriously challenging macro programming with a modicum of readability, you should take a look at the Boost preprocessor library. After all, it wouldn't be C++ if there weren't three completely Turing compatible levels of programming (preprocessor, template metaprogramming, and base level C++)!
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)
You know at the point of call how many args you're going to pass in so there's really no need for overloading.
You can use BOOST_PP_OVERLOAD from a boost library.
Example from official boost doc:
#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>
#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)
#if !BOOST_PP_VARIADICS_MSVC
#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)
#else
// or for Visual C++
#define MACRO_ADD_NUMBERS(...) \
BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9
Depending on what you need, you could do it with var args with macros. Now, optional parameters or macro overloading, there is no such thing.
Not directly answering the question, but using the same trick as David Sorkovsky answer and giving a clear example of how to build complex macros.
Just compile this with g++ -E test.cpp -o test && cat test:
// #define GET_FIRST_ARG_0_ARGS(default) (default)
// #define GET_FIRST_ARG_1_ARGS(default, a) (a)
// #define GET_FIRST_ARG_2_ARGS(default, a, b) (a)
// #define GET_FIRST_ARG_3_ARGS(default, a, b, c) (a)
// #define GET_FIRST_ARG_4_ARGS(default, a, b, c, d) (a)
#define GET_FIRST_ARG_MACROS(default, a, b, c, d, macro, ...) macro
#define GET_FIRST_ARG(default, ...) GET_FIRST_ARG_MACROS( \
,##__VA_ARGS__, \
GET_FIRST_ARG_4_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_3_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_2_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_1_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_0_ARGS(default, ##__VA_ARGS__), \
)
"0,"; GET_FIRST_ARG(0);
"0,1"; GET_FIRST_ARG(0,1);
"0,1,2"; GET_FIRST_ARG(0,1,2);
"0,1,2,3"; GET_FIRST_ARG(0,1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG(0,1,2,3,4);
To see the output:
# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/x86_64-linux-gnu/include/stdc-predef.h" 1 3
# 1 "<command-line>" 2
# 1 "test.cpp"
# 16 "test.cpp"
"0,"; GET_FIRST_ARG_0_ARGS(0);
"0,1"; GET_FIRST_ARG_1_ARGS(0, 1);
"0,1,2"; GET_FIRST_ARG_2_ARGS(0, 1,2);
"0,1,2,3"; GET_FIRST_ARG_3_ARGS(0, 1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG_4_ARGS(0, 1,2,3,4);
Now, a full working program would be:
#include <iostream>
#define GET_FIRST_ARG_0_ARGS(default) (default)
#define GET_FIRST_ARG_1_ARGS(default, a) (a)
#define GET_FIRST_ARG_2_ARGS(default, a, b) (a)
#define GET_FIRST_ARG_3_ARGS(default, a, b, c) (a)
#define GET_FIRST_ARG_4_ARGS(default, a, b, c, d) (a)
#define GET_FIRST_ARG_MACROS(default, a, b, c, d, macro, ...) macro
#define GET_FIRST_ARG(default, ...) GET_FIRST_ARG_MACROS( \
,##__VA_ARGS__, \
GET_FIRST_ARG_4_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_3_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_2_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_1_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_0_ARGS(default, ##__VA_ARGS__), \
)
int main(int argc, char const *argv[]) {
"0,"; GET_FIRST_ARG(0);
"0,1"; GET_FIRST_ARG(0,1);
"0,1,2"; GET_FIRST_ARG(0,1,2);
"0,1,2,3"; GET_FIRST_ARG(0,1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG(0,1,2,3,4);
std::cerr << "0, == " << GET_FIRST_ARG(0) << std::endl;
std::cerr << "0,1 == " << GET_FIRST_ARG(0,1) << std::endl;
std::cerr << "0,1,2 == " << GET_FIRST_ARG(0,1,2) << std::endl;
std::cerr << "0,1,2,3 == " << GET_FIRST_ARG(0,1,2,3) << std::endl;
std::cerr << "0,1,2,3,4 == " << GET_FIRST_ARG(0,1,2,3,4) << std::endl;
return 0;
}
Which would output the following by being compiled with g++ test.cpp -o test && ./test:
0, == 0
0,1 == 1
0,1,2 == 1
0,1,2,3 == 1
0,1,2,3,4 == 1
Note: It is important to use () around the macro contents as #define GET_FIRST_ARG_1_ARGS(default, a) (a) to not break in ambiguous expressions when a is just not a integer.
Extra macro for second argument:
#define GET_SECOND_ARG_0_ARGS(default) (default)
#define GET_SECOND_ARG_1_ARGS(default, a) (default)
#define GET_SECOND_ARG_2_ARGS(default, a, b) (b)
#define GET_SECOND_ARG_3_ARGS(default, a, b, c) (b)
#define GET_SECOND_ARG_4_ARGS(default, a, b, c, d) (b)
#define GET_SECOND_ARG_MACROS(default, a, b, c, d, macro, ...) macro
#define GET_SECOND_ARG(default, ...) GET_SECOND_ARG_MACROS( \
,##__VA_ARGS__, \
GET_SECOND_ARG_4_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_3_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_2_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_1_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_0_ARGS(default, ##__VA_ARGS__), \
)
None of the above examples (from Derek Ledbetter, David Sorkovsky, and Joe D) to count arguments with macros worked for me using Microsoft VCC 10. The __VA_ARGS__ argument is always considered as a single argument (token-izing it with ## or not), so the argument shifting in which those examples rely doesn't work.
So, short answer, as stated by many others above: no, you can't overload macros or use optional arguments on them.
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)
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.
#define \
foo(x) \
({ \
int xx = (x); \
int result = (xx > 32) ? xx : (2*xx); \
result; \
})
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.
Consider:
int x = rand()
int y = foo( x );
x is not known at compile time.
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...