How can I make Xcode consider the values of my defines to fold my code properly? Xcode seems to consider some things that aren't compile as part of the code -- with causes some issues for me :(
Example:
#include <stdio.h>
#define LIE_TO_THE_USER 1
void foobar(int argc)
{ // A
#if LIE_TO_THE_USER
if (0) { // B
#else
if (argc > 0) { // C
#endif
printf("argc is greater than 0\n");
} // D
else
{ // E
printf("argc is not greater than 0\n");
} // F
} // G
int main(int argc, const char * argv[])
{
foobar(argc);
return 0;
}
This code compiles... but is a pain to work with.
As far as Xcode is concerned, it folds brackets C and D together, E and F together, and folds C and G together (instead of folding A and G). Additionally, anything declared after the #if #else #endif (so just the main is this case) doesn't show up in this case.
In Visual Studio, it would just grey out the if (argc> 0 ) { // C and ignore it. But Xcode seems to consider it part of the code. I have found 2 workarounds:
void foobar(int argc)
{
#if LIE_TO_THE_USER
if (0)
#else
if (argc > 0)
#endif
{
printf("argc is greater than 0\n");
}
else
{
printf("argc is not greater than 0\n");
}
}
/// OR
void foobar(int argc)
{
#if LIE_TO_THE_USER
if (0) {
#else
if (argc > 0) {
#endif
printf("argc is greater than 0\n");
}
else
{
printf("argc is not greater than 0\n");
}
#if 0
}
#endif
}
However, this problem is veeeeery frequent in my project. So I would like to find to solve this problem automatically without having to worry about adding workarounds. Does anyone know a way to configure Xcode to ignore lines such as if (argc > 0) {?
Thanks in advance :)
This is one of the reasons macros are horrible; You can arbitrarily mess with the token stream and aren't bound to respect the language's structure.
Visual Studio is just completely ignoring whatever is eaten by the preprocessor, which means it reliably understands the structure of the code under the current defines but has no understanding of the code that's disabled. Xcode is trying to be smarter and to understand the entire program, but this works poorly when you use macros that really subvert the language's structure.
C++ is already hard enough for tools to work with, and the fundamental nature of the preprocessor makes it worse by essentially making it so that the same C++ source can produce arbitrarily many programs. You can avoid this problem by minimizing use of the preprocessor, and restricting uses to the simplest and most conventional, so that tools are likely to understand them.
In this case I think I would go with something like:
void foobar(int argc)
{
static constexpr bool tell_the_truth = false;
if (tell_the_truth && argc > 0)
{
printf("argc is greater than 0\n");
}
else
{
printf("argc is not greater than 0\n");
}
}
Or if you want to be able to configure this from the compilation command you can use:
static constexpr bool tell_the_truth = !LIE_TO_THE_USER
So that you can use a flag such as -DLIE_TO_THE_USER=true or -DLIE_TO_THE_USER=false.
The benefit of this approach is that tools' understanding of this program, the AST, actually represents the intended program and doesn't fundamentally change between configurations.
You can reduce the amount of code that looks like conditional compilation to Xcode by using a conditionally compiled macro, like this:
#define LIE_TO_THE_USER 1
...
#if LIE_TO_THE_USER
#define CONDITIONAL_LIE(A,B) (A)
#else
#define CONDITIONAL_LIE(A,B) (B)
#endif
With this macro in hand, you can rewrite your if as follows:
if (CONDITIONAL_LIE(0, argc > 0))
{
printf("argc is greater than 0\n");
}
else
{
printf("argc is not greater than 0\n");
}
The code would conditionally compile into the same output, but Xcode would think it's a regular if. This also looks cleaner to the reader.
Related
Is the following idea possible in C?
I am reaching failed. (using online c compiler to test this)
Maybe it is solvable using GOTO, but that is not desired.
Just theoretical, if there is any other solution i am missing, but the idea is to have a statemachine just be a little bit more efficient.
#include <stdio.h>
#include <stdint.h>
uint8_t shouldhavebreaked = 0;
#define BREAK_CONDITIONAL(x,y) do { if(x == y) { shouldhavebreaked = 1; break ;} } while(0)
int main()
{
uint8_t swh = 0;
switch (swh)
{
case 0:
/*if state is same, break, otherwise fall-through*/
BREAK_CONDITIONAL(swh, 0);
case 1:
printf("failed \n");
break;
}
printf("changed? %d \n",shouldhavebreaked);
return 0;
}
Instead of the funky do{}while() you could use the less funky if(){} else:
#define BREAK_CONDITIONAL(x,y) if(x == y) { shouldhavebreaked = 1; break; } else
And it is a good habit to over-parenthesize arguments in macros:
#define BREAK_CONDITIONAL(x,y) if((x)==(y)) { shouldhavebreaked=1;break;} else
Those kinds of macros do not make any sense (unless you want to make code more difficult to read, understand and maintain. It will be more error-prone as well). Avoid as a plague. They look like functions and they are not.
If I hide the macro from the #wildplasser answer can you guess what this nonsense does?
if(c)
{
BREAK_CONDITIONAL(a,b) c = a+b;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ANSI C equivalent of try/catch?
Is there a way to skip critical code ? More or less like try-catch in modern programming languages. Just now I'm using this technique to spot errors:
bindSignals();
{
signal(SIGFPE, sigint_handler);
// ...
}
int main(void)
{
bindsignals();
int a = 1 / 0; // division by zero, I want to skip it
return 0;
}
The problem is if I don't exit the program in the handler I get the very same error again and again. If possible I would like to avoid goto. I also heard about "longjump" or something. Is it worth to (learn to) use ?
Well, you can probably accomplish something like that using longjmp(), yes.
Possibly with the "help" of some macros. Note the comment on the manual page, though:
longjmp() and siglongjmp() make programs hard to understand and maintain. If possible an alternative should be used.
I'll throw my two cents in on this. C does not have a mechanism like the try/catch that other languages support. You can build something using setjmp() and longjmp() that will be similar, but nothing exactly the same.
Here's a link showing a nice way of using setjmp and longjmp to do what you were thinking; and a code snippet from the same source:
jmp_buf jumper;
int SomeFunction(int a, int b)
{
if (b == 0) // can't divide by 0
longjmp(jumper, -3);
return a / b;
}
void main(void)
{
if (setjmp(jumper) == 0)
{
int Result = SomeFunction(7, 0);
// continue working with Result
}
else
printf("an error occured\n");
}
I'm currently going from C to Java and I'm having a hard time understanding why you'd use try/catch in the first place. With good error checking you should be fine, always always always use the values that are returned from functions, check the errono values, and validate any user input.
I'm done. That's how my code looks like now. Almost like Java and C#.
#include <setjmp.h>
jmp_buf jumper;
#define try if (setjmp(jumper) == 0)
#define catch else
#define skip_to_catch longjmp(jumper, 0)
static void sigint_handler(int sig)
{
skip_to_catch;
}
int main(void)
{
// init error handling once at the beginning
signal(SIGFPE, sigint_handler);
try
{
int a = 1 / 0;
}
catch
{
printf("hello error\n");
}
return 0;
}
I would like to force a functions parameters to accept only specific definitions. For example, consider #define OUTPUT 1, #define INPUT 0 and void restrictedFunction(int parameter); .
How would I force restrictedFunction(int parameter) to accept only OUTPUT or INPUT?
I would also like to take into consideration that another definition may have the same value, for example, #define LEFT 1 and #define RIGHT 0.
So in this case I would like restrictedFunction(int parameter) to be able to accept only OUTPUT and INPUT specifically.
typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;
void restrictedFunction(IO_Type parameter) { ... }
It doesn't absolutely force the use of the values (the compiler will let someone write restrictedFunction(4)), but it is about as good as you'll get.
If you truly want to force the correct type, then:
typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;
typedef struct { IO_Type io_type } IO_Param;
void restrictedFunction(IO_Param parameter) { ... }
In C99 or later, you could call that with:
restrictedFunction((IO_Param){ INPUT });
This is a compound literal, creating a structure on the fly. It is not entirely clear that the structure type really buys you very much, but it will force the users to think a little and may improve the diagnostics from the compiler when they use it wrong (but they can probably use restrictedFunction((IO_Param){ 4 }); still).
What this means is that your restrictedFunction() code should be ready to validate the argument:
void restrictedFunction(IO_Type io_type)
{
switch (io_type)
{
case INPUT:
...do input handling...
break;
case OUTPUT:
...do output handling...
break;
default:
assert(io_type != INPUT && io_type != OUTPUT);
...or other error handling...
break;
}
}
You could use an enum.
typedef enum TrafficDirection { INPUT = 0, OUTPUT = 1 } TrafficDirection;
restrictedFunction(TrafficDirection direction);
of course, this isn't perfect. You can still pass any int to it as long as you use a cast.
restrictedFunction((TrafficDirection) 4);
You don't get quite as much protection as you might like, but you can do:
enum func_type { INPUT, OUTPUT };
void restrictedFunction( enum func_type parameter );
You can use a wrapper to validate the argument:
#define restrictedFunction(x) do { \
static_assert((x) == INPUT || (x) == OUTPUT); \
assert(!strcmp(#x, "INPUT") || !strcmp(#x, "OUTPUT")); \
restrictedFunction(x); \
} while(0)
Notes:
This assumes restrictedFunction() returns a void. If it returns a value which you actually use, you'll need something like gcc's compound statement http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html. Or--better--you can use BUILD_BUG_ON_ZERO (see What is ":-!!" in C code?), which I keep forgetting about, because it doesn't seem to work with C++.
The do ... while(0) is to "swallow the semi-colon"; not really relevant here.
static_assert() is a compile-time assert; there are many variants available. Here is a link to one, https://stackoverflow.com/a/9059896/318716, if you don't have your own handy.
assert() is the standard run-time assert.
With gcc 4.1.2, and my version of static_assert(), you can replace the run-time assert() with a compile-time assert when the two !strcmp()'s are replaced with ==; see example below. I haven't tested this with other compilers.
x is only used once in the macro expansion, since the first four references are only used at compile-time.
When your actually define your function, you'll have to add parentheses to disable the macro expansion, as in:
void (restrictedFunction)(int x){ ... }
Also, if your code has a special case (whose code doesn't?) where you need to call restrictedFunction() with the argument foo, you'll need to write:
(restrictedFunction)(foo);
Here is a complete example, which puts a wrapper around the standard library function exit():
#include <stdlib.h>
#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}
#define exit(x) do { \
ASSERTM((x) == EXIT_SUCCESS || (x) == EXIT_FAILURE, value); \
ASSERTM(#x == "EXIT_SUCCESS" || #x == "EXIT_FAILURE", symbol); \
exit(x); \
} while(0)
int main(void) {
exit(EXIT_SUCCESS); // good
exit(EXIT_FAILURE); // good
exit(0); // bad
exit(3); // doubly bad
}
If I try to compile it, I get:
gcc foo.c -o foo
foo.c: In function 'main':
foo.c:17: error: enumerator value for 'symbol_ASSERT_line_17' is not an integer constant
foo.c:18: warning: division by zero
foo.c:18: error: enumerator value for 'value_ASSERT_line_18' is not an integer constant
foo.c:18: error: enumerator value for 'symbol_ASSERT_line_18' is not an integer constant
I'm trying to learn C by writing a simple parser / compiler. So far its been a very enlightening experience, however coming from a strong background in C# I'm having some problems adjusting - in particular to the lack of exceptions.
Now I've read Cleaner, more elegant, and harder to recognize and I agree with every word in that article; In my C# code I avoid throwing exceptions whenever possible, however now that I'm faced with a world where I can't throw exceptions my error handling is completely swamping the otherwise clean and easy-to-read logic of my code.
At the moment I'm writing code which needs to fail fast if there is a problem, and it also potentially deeply nested - I've settled on a error handling pattern whereby "Get" functions return NULL on an error, and other functions return -1 on failure. In both cases the function that fails calls NS_SetError() and so all the calling function needs to do is to clean up and immediately return on a failure.
My issue is that the number of if (Action() < 0) return -1; statements that I have is doing my head in - it's very repetitive and completely obscures the underlying logic. I've ended up creating myself a simple macro to try and improve the situation, for example:
#define NOT_ERROR(X) if ((X) < 0) return -1
int NS_Expression(void)
{
NOT_ERROR(NS_Term());
NOT_ERROR(Emit("MOVE D0, D1\n"));
if (strcmp(current->str, "+") == 0)
{
NOT_ERROR(NS_Add());
}
else if (strcmp(current->str, "-") == 0)
{
NOT_ERROR(NS_Subtract());
}
else
{
NS_SetError("Expected: operator");
return -1;
}
return 0;
}
Each of the functions NS_Term, NS_Add and NS_Subtract do a NS_SetError() and return -1 in the case of an error - its better, but it still feels like I'm abusing macros and doesn't allow for any cleanup (some functions, in particular Get functions that return a pointer, are more complex and require clean-up code to be run).
Overall it just feels like I'm missing something - despite the fact that error handling in this way is supposedly easier to recognize, In many of my functions I'm really struggling to identify whether or not errors are being handled correctly:
Some functions return NULL on an error
Some functions return < 0 on an error
Some functions never produce an error
My functions do a NS_SetError(), but many other functions don't.
Is there a better way that I can structure my functions, or does everyone else also have this problem?
Also is having Get functions (that return a pointer to an object) return NULL on an error a good idea, or is it just confusing my error handling?
It's a bigger problem when you have to repeat the same finalizing code before each return from an error. In such cases it is widely accepted to use goto:
int func ()
{
if (a() < 0) {
goto failure_a;
}
if (b() < 0) {
goto failure_b;
}
if (c() < 0) {
goto failure_c;
}
return SUCCESS;
failure_c:
undo_b();
failure_b:
undo_a();
failure_a:
return FAILURE;
}
You can even create your own macros around this to save you some typing, something like this (I haven't tested this though):
#define CALL(funcname, ...) \
if (funcname(__VA_ARGS__) < 0) { \
goto failure_ ## funcname; \
}
Overall, it is a much cleaner and less redundant approach than the trivial handling:
int func ()
{
if (a() < 0) {
return FAILURE;
}
if (b() < 0) {
undo_a();
return FAILURE;
}
if (c() < 0) {
undo_b();
undo_a();
return FAILURE;
}
return SUCCESS;
}
As an additional hint, I often use chaining to reduce the number of if's in my code:
if (a() < 0 || b() < 0 || c() < 0) {
return FAILURE;
}
Since || is a short-circuit operator, the above would substitute three separate if's. Consider using chaining in a return statement as well:
return (a() < 0 || b() < 0 || c() < 0) ? FAILURE : SUCCESS;
One technique for cleanup is to use an while loop that will never actually iterate. It gives you goto without using goto.
#define NOT_ERROR(x) if ((x) < 0) break;
#define NOT_NULL(x) if ((x) == NULL) break;
// Initialise things that may need to be cleaned up here.
char* somePtr = NULL;
do
{
NOT_NULL(somePtr = malloc(1024));
NOT_ERROR(something(somePtr));
NOT_ERROR(somethingElse(somePtr));
// etc
// if you get here everything's ok.
return somePtr;
}
while (0);
// Something went wrong so clean-up.
free(somePtr);
return NULL;
You lose a level of indentation though.
Edit: I'd like to add that I've nothing against goto, it's just that for the use-case of the questioner he doesn't really need it. There are cases where using goto beats the pants off any other method, but this isn't one of them.
You're probably not going to like to hear this, but the C way to do exceptions is via the goto statement. This is one of the reasons it is in the language.
The other reason is that goto is the natural expression of the implementation of a state machine. What common programming task is best represented by a state machine? A lexical analyzer. Look at the output from lex sometime. Gotos.
So it sounds to me like now is the time for you to get chummy with that parriah of language syntax elements, the goto.
Besides goto, standard C has another construct to handle exceptional flow control setjmp/longjmp. It has the advantage that you can break out of multiply nested control statements more easily than with break as was proposed by someone, and in addition to what goto provides has a status indication that can encode the reason for what went wrong.
Another issue is just the syntax of your construct. It is not a good idea to use a control statement that can inadvertibly be added to. In your case
if (bla) NOT_ERROR(X);
else printf("wow!\n");
would go fundamentally wrong. I'd use something like
#define NOT_ERROR(X) \
if ((X) >= 0) { (void)0; } \
else return -1
instead.
THis must be thought on at least two levels: how your functions interact, and what you do when it breaks.
Most large C frameworks I see always return a status and "return" values by reference (this is the case of the WinAPI and of many C Mac OS APIs). You want to return a bool?
StatusCode FooBar(int a, int b, int c, bool* output);
You want to return a pointer?
StatusCode FooBar(int a, int b, int c, char** output);
Well, you get the idea.
On the calling function's side, the pattern I see the most often is to use a goto statement that points to a cleanup label:
if (statusCode < 0) goto error;
/* snip */
return everythingWentWell;
error:
cleanupResources();
return somethingWentWrong;
What about this?
int NS_Expression(void)
{
int ok = 1;
ok = ok && NS_Term();
ok = ok && Emit("MOVE D0, D1\n");
ok = ok && NS_AddSub();
return ok
}
The short answer is: let your functions return an error code that cannot possibly be a valid value - and always check the return value. For functions returning pointers, this is NULL. For functions returning a non-negative int, it's a negative value, commonly -1, and so on...
If every possible return value is also a valid value, use call-by-reference:
int my_atoi(const char *str, int *val)
{
// convert str to int
// store the result in *val
// return 0 on success, -1 (or any other value except 0) otherwise
}
Checking the return value of every function might seem tedious, but that's the way errors are handled in C. Consider the function nc_dial(). All it does is checking its arguments for validity and making a network connection by calling getaddrinfo(), socket(), setsockopt(), bind()/listen() or connect(), finally freeing unused resources and updating metadata. This could be done in approximately 15 lines. However, the function has nearly 100 lines due to error checking. But that's the way it is in C. Once you get used to it, you can easily mask the error checking in your head.
Furthermore, there's nothing wrong with multiple if (Action() == 0) return -1;. To the contrary: it is usually a sign of a cautious programmer. It's good to be cautious.
And as a final comment: don't use macros for anything but defining values if you can't justify their use while someone is pointing with a gun at your head. More specifically, never use control flow statements in macros: it confuses the shit out of the poor guy who has to maintain your code 5 years after you left the company. There's nothing wrong with if (foo) return -1;. It's simple, clean and obvious to the point that you can't do any better.
Once you drop your tendency to hide control flow in macros, there's really no reason to feel like you're missing something.
A goto statement is the easiest and potentially cleanest way to implement exception style processing. Using a macro makes it easier to read if you include the comparison logic inside the macro args. If you organize the routines to perform normal (i.e. non-error) work and only use the goto on exceptions, it is fairly clean for reading. For example:
/* Exception macro */
#define TRY_EXIT(Cmd) { if (!(Cmd)) {goto EXIT;} }
/* My memory allocator */
char * MyAlloc(int bytes)
{
char * pMem = NULL;
/* Must have a size */
TRY_EXIT( bytes > 0 );
/* Allocation must succeed */
pMem = (char *)malloc(bytes);
TRY_EXIT( pMem != NULL );
/* Initialize memory */
TRY_EXIT( initializeMem(pMem, bytes) != -1 );
/* Success */
return (pMem);
EXIT:
/* Exception: Cleanup and fail */
if (pMem != NULL)
free(pMem);
return (NULL);
}
It never occurred to me to use goto or do { } while(0) for error handling in this way - its pretty neat, however after thinking about it I realised that in many cases I can do the same thing by splitting the function out into two:
int Foo(void)
{
// Initialise things that may need to be cleaned up here.
char* somePtr = malloc(1024);
if (somePtr = NULL)
{
return NULL;
}
if (FooInner(somePtr) < 0)
{
// Something went wrong so clean-up.
free(somePtr);
return NULL;
}
return somePtr;
}
int FooInner(char* somePtr)
{
if (something(somePtr) < 0) return -1;
if (somethingElse(somePtr) < 0) return -1;
// etc
// if you get here everything's ok.
return 0;
}
This does now mean that you get an extra function, but my preference is for many short functions anyway.
After Philips advice I've also decided to avoid using control flow macros as well - its clear enough what is going on as long as you put them on one line.
At the very least Its reassuring to know that I'm not just missing something - everyone else has this problem too! :-)
Use setjmp.
http://en.wikipedia.org/wiki/Setjmp.h
http://aszt.inf.elte.hu/~gsd/halado_cpp/ch02s03.html
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
#include <setjmp.h>
#include <stdio.h>
jmp_buf x;
void f()
{
longjmp(x,5); // throw 5;
}
int main()
{
// output of this program is 5.
int i = 0;
if ( (i = setjmp(x)) == 0 )// try{
{
f();
} // } --> end of try{
else // catch(i){
{
switch( i )
{
case 1:
case 2:
default: fprintf( stdout, "error code = %d\n", i); break;
}
} // } --> end of catch(i){
return 0;
}
#include <stdio.h>
#include <setjmp.h>
#define TRY do{ jmp_buf ex_buf__; if( !setjmp(ex_buf__) ){
#define CATCH } else {
#define ETRY } }while(0)
#define THROW longjmp(ex_buf__, 1)
int
main(int argc, char** argv)
{
TRY
{
printf("In Try Statement\n");
THROW;
printf("I do not appear\n");
}
CATCH
{
printf("Got Exception!\n");
}
ETRY;
return 0;
}
If I use the macro:
#define AND
in the following way:
if(...)
{
...
}
elseANDif(...)
{
...
}
What output does the preprocessor produce?
Edit:
I intend to use:
#define TEST(params) if(...){...}else
the ... in if(...) is a complicated expression using params
the ... in {...} performs some operations & is independent of params
#define AND
TEST(x1) AND TEST(x2)
{
//the code for the final else
}
Is the AND helping here or can I do without it?
No, this isn't going to work as you expect. And you can test what the preprocessor does by running your code through cpp.
eliben#eliben-desktop:~/temp$ cat z.c
#define AND
if(...)
{
...
}
elseANDif(...)
{
...
}
eliben#eliben-desktop:~/temp$ cpp z.c
# 1 "z.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "z.c"
if(...)
{
...
}
elseANDif(...)
{
...
}
The technical reason is that when cpp expands macros it looks for a complete identifier token matching this macro's name. I.e. in your case, it looks for the identifier AND. However when it parses the code it doesn't find such an identifier. It finds elseANDif which is quite a different identifier. It has no way to break elseANDif into constituents, and that's a good thing because otherwise macros would work very badly. Imagine:
const int FUSIONDEBUG = 5;
Whatever that means, in real C code this would break awfully, since NDEBUG is almost always defined in production code to be empty (google on what NDEBUG means).
Regarding your edit, the best advice I can give you on such matters is DON'T DO IT. (ab)Using macros like this may appear at first to make the code more readable, but in the long term it makes it much less readable, with the added peril that macros are tricky to get perfectly right and with certain combination of tokens can blow up on you badly.
So you can definitely do without the AND as well as without the TEST altogether.
This:
#define TEST(condn) if(...){...}else
is nonsense; what do you expect the ellipses (...) to do!?
the example usage you gave would expand to
if(...){...} else if(...){...}else
{
//the code for the final else
}
which is patently nonsense; where is the condn argument used?. Either way whatever you really intended the AND has no effect other than dummy readability. If you are trying to invent a new language, the C preprocessor is not the way to do this. I can see no advantage to what you appear to be trying to achieve over more straightforward code.
If you intended:
#define TEST(condn) if(condn){/*some code*/}else
then how is the resultant:
if(a==b){/*some code*/} else if(b==c){/*some code*/}else
{
//the code for the final else
}
better than:
if(a==b || b==c)
{
/*some code*/
}
else
{
//the code for the final else
}
where /*some code*/ is not unnecessarily duplicated?
Note that here the single condition chained by || is equivalent to your multiple conditions chained by else if, so even if you use the TEST macro, there is no need to use it that way when:
TEST( a==b || b==c)
{
//the code for the final else
}
will suffice.
Macros are often ill-advised at the best of times, but you have chosen a particularly prime example of macro abuse! Consider for example how you might debug such code in a source-level debugger.
The short answer to your question is "yes". You can certainly do what you are suggesting. Here is a basic, working example:
#include <stdio.h>
#define AND
#define TEST(params) if (!params) { printf(#params " was false\n"); } else
int main(int argc, char ** argv)
{
int param_1 = 1;
int param_2 = 0;
TEST(param_1) AND TEST(param_2)
{
printf("success!\n");
}
}
After macro expansion, the code would essentially look like this:
int main(int argc, char ** argv)
{
int param_1 = 1;
int param_2 = 0;
if (!param_1) { printf("param_1 was false\n"); } else
if (!param_2) { printf("param_2 was false\n"); } else
{
printf("success!\n");
}
}
As pointed out by others, doing something like this is questionable because it messes with the way people read code and can make future debugging difficult. In a case such as this, I would definitely recommend using a function call if at all possible. This is the approach that Secure recommends in his comment:
int test_parameters(int params)
{
if (!params) { printf("bad parameters"); return 0; }
else { return 1; }
}
int main(int argc, char ** argv)
{
int param_1 = 1;
int param_2 = 0;
if (test_parameters(param_1) && test_parameters(param_2))
{
printf("success!\n");
}
}