A quick answer for this question will be
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND)\
while(scanf(" "FORM, X)<1 || !(COND))
printf("Invalid input, enter again: ");
calling the above macro in the code with the following way
int a;
SCAN_ONEENTRY_WITHCHECK("%d", &a, (a>3 && a<15))
is equivalent to this
int a;
while(scanf(" %d", &a)<1 || !(a>3 && a<15))
printf("Invalid input, enter again: ");
but the above answer is wrong because if the user enter for example aaa
as input then the above code will result an infinite loop because the stdin is not cleaned because the aaa
input is not catched by the scanf(" %d", &a)
. So we have to add something that clean the stdin if the user enter a such input. adding scanf("%*[^\n]")
could be a solution for that. and the above code will be
int a;
while(scanf(" %d", &a)<1 || !(a>3 && a<15)) {
scanf("%*[^\n]"); // clean stdin
printf("Invalid input, enter again: ");
}
but the above code still contains a limitation. For example If the user enter a valid integer (20
) and the integr does not respect the condition (a>3 && a<15)
. In this case we do not have to clean the stdin because it's already cleaned otherwise the user will be asked for input data for 2 times . tis problem coud solved with the following solution:
int a;
int c;
while((c=(scanf(" %d", &a)<1)) || !(a>3 && a<15)) {
if (c) scanf("%*[^\n]"); // clean stdin
printf("Invalid input, enter again: ");
}
The above code does not respect the criteria input data should be followed by white space
for example if the user enter 10abc
as input then the above code will catch 10 as input integer and it will clean the rest abc
. this problem could be solved if we check that the next charachter is a white space (with isspace()
function) nothing else
int a;
char tmp;
int c;
while((c=(scanf(" %d%c", &a, &tmp)!=2 || !isspace(tmp))) || !(a>3 && a<15)) {
if (c) scanf("%*[^\n]"); // clean stdin
printf("Invalid input, enter again: ");
}
And now if we want to go back to the macro. the macro code will be:
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND) \
do {\
char tmp;\
int c;\
while ((c=(scanf(" "FORM"%c", X, &tmp)!=2 || !isspace(tmp)))\
|| !(COND)) {\
if (c) scanf("%*[^\n]");\
printf("Invalid input, please enter again: ");\
}\
} while(0)
Adding do {...} while(0)
in the macro could be explained by this link
The above macro could be writen in another way
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND) \
do {\
char tmp;\
while(((scanf(" "FORM"%c",X,&tmp)!=2 || !isspace(tmp)) && !scanf("%*[^\n]"))\
|| !(COND)) {\
printf("Invalid input, please enter again: ");\
}\
} while(0)
Examples of using the macro:
int main()
{
int decision;
double q;
char buf[32];
printf("Input data, valid choice 1 or 0: ");
SCAN_ONEENTRY_WITHCHECK("%d",&decision,(decision==0 || decision==1));
printf("You have entered good input : %d\n", decision);
printf("Input unsigned double: ");
SCAN_ONEENTRY_WITHCHECK("%lf",&q, (q == (unsigned int) q));
printf("You have entered good input : %lf\n", q);
printf("Input name: ");
SCAN_ONEENTRY_WITHCHECK("%s",buf, (strcmp(buf,"kallel")==0));
printf("You have entered good input : %s\n", buf);
printf("Input data should be valid integer: ");
SCAN_ONEENTRY_WITHCHECK("%d",&decision,1);
// COND is 1 ==> we do not have any check in the input integer
printf("You have entered good input : %d\n", decision);
}
Execution
$ ./test
Input data, valid choice 1 or 0: 4
Invalid input, please enter again: a4
Invalid input, please enter again: a1
Invalid input, please enter again: 1a
Invalid input, please enter again: 1
You have entered good input : 1
Input unsigned double: 2.3
Invalid input, please enter again: a.0a
Invalid input, please enter again: 2.0a
Invalid input, please enter again: a2.0
Invalid input, please enter again: 2.0
You have entered good input : 2.000000
Input name: an
Invalid input, please enter again: anyad
Invalid input, please enter again: adny
Invalid input, please enter again: any
You have entered good input : any
Input data should be valid integer: 2.5
Invalid input, please enter again: -454f
Invalid input, please enter again: -454
You have entered good input : -454