Based on an answer of the following question: How do I transform an IF statement with 2 variables onto a switch function using C?
I want to develop SWITCH and CASE macros to use it (like switch and case) for strings.
Some thing like that:
char buf[256];
SWITCH (buf) {
CASE ("abcdef"):
printf ("A1!\n");
BREAK;
CASE ("ghijkl"):
printf ("B1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
what could be SWITCH and CASE and BREAK and DEFAULT here ?
If you really want it, well, here it is:
#include <string.h>
#include <stdio.h>
const char *kludge;
#define evilmacro(line) label##line
#define fakelabel(line) evilmacro(line)
#define SWITCH(str) while((kludge = (str)))
#define CASE(str) if(strcmp(kludge, str) == 0) { fakelabel(__LINE__)
#define BREAK break; /* out of while loop */ }
#define DEFAULT if(1) { fakelabel(__LINE__)
int main(int argc, char *argv[]) {
SWITCH (argv[1]) {
CASE ("abcdef"):
printf ("A1!\n");
BREAK;
CASE ("ghijkl"):
printf ("B1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
return 0;
}
Getting rid of the unused labels is left as an exercise for the reader :)
EDIT: fwiw, what I would really do is table driven code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SWITCH(S) char *_S = S; if (0)
#define CASE(S) } else if (strcmp(_S, S) == 0) {switch(1) { case 1
#define BREAK }
#define DEFAULT } else {switch(1) { case 1
int main()
{
char buf[256];
printf("\nString - Enter your string: ");
scanf ("%s", buf);
SWITCH (buf) {
CASE ("abcdef"):
printf ("B1!\n");
BREAK;
CASE ("ghijkl"):
printf ("C1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
}
Related
I am attempting to parse a command line argument, which in turn will execute an associated case within a switch statement. When I parse an integer argument (as seen in the code below), the associated case executes correctly. When I attempt to parse a string such as "CPU", I do not get the correct output.
Functioning code (parsing an integer e.g. an argument of 4 gives athe correct output of hello):
#include <stdio.h>
int main(int argc, char *argv[]) {
char execution_mode = atoi (argv[1]);
switch (execution_mode)
{
case (4) :
printf("Hello");
getchar();
break;
case (8) :
printf("Goodbye");
getchar();
break;
default:
printf("Error! execution mode is not correct");
getchar();
break;
}
return 0;
}
My attempt at parsing a string e.g. the argumentCPU:
#include <stdio.h>
int main(int argc, char *argv[]) {
typedef enum MODE { CPU, OPENMP } MODE;
MODE execution_mode = (char)argv[1];
switch (execution_mode)
{
case (CPU) :
printf("Hello");
getchar();
break;
case (OPENMP) :
printf("Goodbye");
getchar();
break;
default:
printf("Error! execution mode is not correct");
getchar();
break;
}
return 0;
}
You cannot convert a string to an enumerate like this. What you're doing is just converting the pointer to the string to char. Which fails.
One alternative (besides comparing first argument with strcmp) to avoid this would be to give a character value to your enumerates:
typedef enum { CPU='C', OPENMP='O' } MODE;
and now you can pick the first letter of the first argument and convert it:
MODE execution_mode = (MODE)argv[1][0];
The letters must be of course all different. And check argc>1 to see if argv[1] is valid, of course
If you want full string match, you have no other choice than using strcmp:
const char *execution_mode = argv[1];
if (strcmp(execution_mode,"CPU")==0)
{
// do something
}
else if (strcmp(execution_mode,"OPENMP")==0)
{
// do something else
}
With the help of the users who have answered this question, I have found a working solution by using strcmp as seen below. I have also added some error checking to ensure enough arguments have been enterred on the command-line.
#include <stdio.h>
int main(int argc, char *argv[]) {
//Ensure there are enough arguments
if (argc < 2)
{
printf("Error: not enough arguments");
exit(1);
}
typedef enum MODE { CPU, OPENMP, CUDA, ALL } MODE;
MODE execution_mode = (MODE)argv[1];
//Compare string with command-line arguments
if (strcmp("CPU", execution_mode) == 0)
{
//selects CPU case
execution_mode = CPU;
}
else if (strcmp("OPENMP", execution_mode) == 0)
{
//selects OPENMP case
execution_mode = OPENMP;
}
else
{
printf("invalid arg");
}
//Switch statement
switch (execution_mode)
{
case (CPU) :
printf("CPU MODE SELECTED");
getchar();
break;
case (OPENMP) :
printf("OPENMP MODE SELECTED");
getchar();
break;
default:
printf("Error: execution mode is not correct");
getchar();
break;
}
return 0;
}
I'm trying to make a program I already made more organized by using functions. the purpose of it is processing a menu with several options for calculating different values. This code is for processing the Main menu, and the error I'm getting is that every character I type comes out as invalid (activates the default case in the switch) even if it is 1, 2 or 3, which are the possible options. What am I doing wrong?
void process_main_menu(){
int c;
print_main_menu();
int option=getchar();
while((c=getchar())!='\n' && c!=EOF);
switch(option){
case 1:
program_state=ST_MENU_BASS;
break;
case 2:
program_state=ST_MENU_TREBLE;
break;
case 3:
program_state=ST_EXIT_PROGRAM;
break;
default:
fprintf(stderr, "%s\n", MSG_INVALID_NUMBER);
program_state=ST_MAIN_MENU;
}
}
I'm updating the code as I see it wasn't complete enough. I'm actually using macros for this
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#define OPT_MENU_BASS 1
#define OPT_MENU_TREBLE 2
#define OPT_EXIT_PROGRAM 3
typedef enum {
ST_MAIN_MENU,
ST_MENU_BASS,
ST_MENU_TREBLE,
ST_EXIT_PROGRAM,
ST_MENU_TREBLE_FREQ,
ST_MENU_TREBLE_GAIN,
ST_MENU_TREBLE_FREQ_FREQ_TREBLE,
ST_MENU_TREBLE_FREQ_RESISTOR_3,
ST_MENU_TREBLE_FREQ_CAPACITOR_3,
ST_MENU_TREBLE_GAIN_RES5,
ST_MENU_BASS_FREQ,
ST_MENU_BASS_GAIN,
ST_MENU_BASS_FREQ_FREQ_BASS,
ST_MENU_BASS_FREQ_RESISTOR_2,
ST_MENU_BASS_FREQ_CAPACITOR_1,
ST_MENU_BASS_GAIN_RESISTOR_1,
} state_t;
state_t program_state;
void process_main_menu(){
int c;
print_main_menu();
char option=getchar();
while((c=getchar())!='\n' && c!=EOF);
switch(option){
case OPT_MENU_BASS:
program_state=ST_MENU_BASS;
break;
case OPT_MENU_TREBLE:
program_state=ST_MENU_TREBLE;
break;
case OPT_EXIT_PROGRAM:
program_state=ST_EXIT_PROGRAM;
break;
default:
fprintf(stderr, "%s\n", MSG_INVALID_NUMBER);
program_state=ST_MAIN_MENU;
}
}
You're reading in a character such as , which is stored as its ASCII code, not a numerical value.
You need to change your cases to look for the character '1', not the number 1.
case '1':
program_state=ST_MENU_BASS;
break;
case '2':
program_state=ST_MENU_TREBLE;
break;
case '3':
program_state=ST_EXIT_PROGRAM;
break;
EDIT:
Given the macros you're using, you need to modify the macros to represent the characters '1', '2' or '3' instead of the numbers 1, 2 or 3.
#define OPT_MENU_BASS '1'
#define OPT_MENU_TREBLE '2'
#define OPT_EXIT_PROGRAM '3'
This is just a simple program I wrote in order to get some practice with getopt, and structs.
typedef struct {
int age;
float body_fat;
} personal;
typedef struct {
const char *name;
personal specs;
} person;
int main(int argc, char *argv[])
{
char c;
person guy;
while((c = getopt(argc, argv, "n:a:b:")) != -1)
switch(c) {
case 'n':
guy.name = optarg;
break;
case 'a':
guy.specs.age = atoi(optarg);
break;
case 'b':
guy.specs.body_fat = atof(optarg);
break;
case '?':
if(optopt == 'a') {
printf("Missing age!\n");
} else if (optopt == 'b') {
printf("Missing body fat!\n");
} else if (optopt == 'n') {
printf("Missing name!\n");
} else {
printf("Incorrect arg!\n");
}
break;
default:
return 0;
}
printf("Name: %s\nAge: %i\nFat Percentage: %2.2f\n",
guy.name, guy.specs.age, guy.specs.body_fat);
return 0;
}
Everything works just fine except for the 'b' option. For some reason specifying that one doesn't change anything. It always returns as 0.0. I don't get why this would be if the other args work just fine.
Your example is missing the header files which would declare the corresponding prototypes. Adding these
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
makes it work for me. I also changed the type for c to int (a char will not hold a -1), and did a
memset(&guy, 0, sizeof(guy));
just to ensure it was a known value. Compiler warnings are your friend. I used this (a script named gcc-normal) to apply warnings:
#!/bin/sh
# $Id: gcc-normal,v 1.4 2014/03/01 12:44:54 tom Exp $
# these are my normal development-options
OPTS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversion"
${ACTUAL_GCC:-gcc} $OPTS "$#"
Though the RCS-identifier is fairly recent, it is an old script which I use in builds.
I wrote a C program wherein I am accepting a numeric input from the user. However, if the user inputs a character, then I have to show that it is an invalid input and make the user enter a number again. How do I achieve this? I am writing the program using gcc compiler in Ubuntu. The search results on Google suggest to use isalpha function...however, it is not available in the libraries I guess.
I also tried the below...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void main()
{
system("clear");
if (isdigit(1))
printf("this is an alphabet\n");
else
printf("this is not an alphabet\n");
}
You will need to use scanf to get user input with %d as you want to scan an integer. In your case, scanf will return 1 on sucessfull scanning.
int num;
//loop to prompt use to enter valid input
while(scanf("%d",&num)==0) //scanning an integer failed
{
printf("invalid input ! Try again\n");
scanf("%*s"); //remove the invalid data from stdin
}
The functions isalpha() and isdigit() works when you are getting a character input using %c in the scanf. If you want to scan input using %c , then you can simply check like what you have done in your code provided that you get input using %c. Note that character 1 ('1') is note equal to integer 1 . Characters have their integer values as represented by the ASCII table. Your program to prompt the user again when the user enters anything other that a number using %c will look like this:
char ch;
while(1){
printf("Enter a number\n");
scanf(" %c",&ch);
printf(Your input is %c\n",ch);
if(isdigit(ch))
{
printf("This is a number\n");
break;
}
else
printf("This is not a number. Invalid input\n");
}
I tried the below code which worked fine..using isdigit()
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
void main()
{
system("clear");
char str[1];
printf("Enter a number\n");
scanf("%s",str);
printf("What you entered was %s\n",str);
if(isdigit(str[0]))
printf("this is not an alphabet\n");
else
printf("this is an alphabet\n");
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include<ctype.h>
int main()
{
char c;
printf("Enter a character: ");
scanf("%c",&c);
bool check=true;
while(check)
{
if( (c>='a'&& c<='z') || (c>='A' && c<='Z'))
{
printf("%c is an alphabet.",c);
check=true;
break;
}
else
{
printf("%c is not an alphabet.",c);
check=false;
}
}
return 0;
}
You can write your own. It's better to check digits since there're less cases for digits.
bool isDigit(char c) {
bool rtn = false;
switch(c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
rtn = true;
break;
default:
rtn = false;
break;
}
return rtn;
}
I have the following code
#define SWITCH(S) char *_S = S; if (0)
#define CASE(S) } else if (strcmp(_S, S) == 0) {switch(1) { case 1
#define BREAK }
#define DEFAULT } else {switch(1) { case 1
int main()
{
char buf[256];
printf("\nString - Enter your string: ");
scanf ("%s", buf);
SWITCH (buf) {
CASE ("abcdef"):
printf ("B1!\n");
BREAK;
CASE ("ghijkl"):
printf ("C1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
}
If I generate the pre-processor code with gcc -E, I will get the following code
int main()
{
char buf[256];
printf("\nString - Enter your string: ");
scanf ("%s", buf);
char *_S = buf;
if (0) {
} else if (strcmp(_S, "abcdef") == 0) {switch(1) { case 1:
printf ("B1!\n");
};
} else if (strcmp(_S, "ghijkl") == 0) {switch(1) { case 1:
printf ("C1!\n");
};
} else {switch(1) { case 1:
printf ("D1!\n");
};
}
}
But for some gcc defining char *_S = buf; in the middle of the code is not wolcome and could provide compilation error
How to fix that in my macro?
Please do not suggest to define char *_S as global (out of the main)
Remove the macros altogether, and write it in the "expanded" way, moving the declaration to the top. These macros are horrific.
Failing that, tweak SWITCH to introduce a new scope (a second {). This will of course force you to have to close two scopes, so perhaps add a SWITCH_END abomination to use at the end, to encapsulate that. Whatever.
Oh no!!!
I wrote this as a joke in this post
Don't use it is very very horrific, if you want to avoid if-else you can do it without torturing the compiler, consider using pair of strings:
#include <stdio.h>
#include <string.h>
int main(void)
{
char buf[256];
const char *ap[] = {
"abcdef", "B1!\n",
"ghijkl", "C1!\n",
NULL , "D1!\n",
}, **p = ap;
printf("\nString - Enter your string: ");
scanf ("%s", buf);
while (*p) {
if (strcmp(buf, *p) == 0) break;
p += 2;
}
printf("%s", *(++p));
return 0;
}
Make sure the code is being compiled as C99 or later; otherwise, you will need to use a different control structure.
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
SWITCH(buf)
{
...
}
#else
if (strcmp(buf, "abcdef") == 0)
{
...
}
else if (strcmp (buf, "ghijkl") == 0)
{
...
}
else
{
...
}
#endif
It's generally not a good idea to use the preprocessor to "alter" or extend C syntax (I have the scar tissue to prove it); switch isn't defined on string expressions for a reason.
If you really want to use a switch in this situation, then it may be better to code up a hash function that returns a key for each string, and switch on the result:
#define ABCDEF ... // hash key generated for "abcdef"
#define GHIJKL ... // hash key generated for "ghijkl"
...
switch(hash(buf))
{
case ABCDEF :
...
break;
case GHIJKL :
...
break;
default:
...
break;
}