(This is homework, however I have a working solution that I'm trying to improve upon)
I'm working on a program that adds or subtracts up to 15 decimal or hexadecimal numbers from the command line. You run the program with the arguments and then choose the option from a menu. I.e.:./main 5 10 15 20
will take in those four arguments and allow you to either add or subtract.
I have a working program, but I am trying to optimize it to reduce some redundancy. Basically my add and subtract functions are identical other than some sign switches, and I feel like there might be a better way. Here is what I have so far (the program can accept decimal input, or hex input formatted with a preceding 0x, which is why there are a sections dealing with those conversions):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void menu(char **options){
for(int i = 0; i < 3; ++i){
printf("%d. %s\n", i, options[i]);
}
printf("Menu Item: ");
}
int isHex(char *argv, int len){
if(argv[0] == '0' && (argv[1] == 'x' || argv[1] == 'X')){
for (int i = 2; i < len; ++i)
if(!isxdigit(argv[i]))
return 0;
return 1;
}
return 0;
}
int convertHex(char *hexVal, int len){
int dec_val = 0;
int base = 1;
for (int i=len-1; i>=2; i--) {
if (hexVal[i]>='0' && hexVal[i]<='9'){
dec_val += (hexVal[i] - 48)*base;
base = base * 16;
}
else if (hexVal[i]>='A' && hexVal[i]<='F') {
dec_val += (hexVal[i] - 55)*base;
base = base*16;
}
}
return dec_val;
}
void add(int argc, char **argv){
int i = 1, s = 0, x = 0;
if(isHex(argv[i], strlen(argv[i])))
s = (x =convertHex(argv[i], strlen(argv[i])));
else
s = atoi(argv[i]);
printf("%s + ", argv[i]);
for(i = 2;i<argc;i++){
if(isHex(argv[i], strlen(argv[i]))){
s += (x = convertHex(argv[i], strlen(argv[i])));
printf("%s", argv[i]);
}
else{
printf("%s", argv[i]);
s += atoi(argv[i]);
}
if(i == argc-1)
printf(" = ");
else
printf(" + ");
}
printf("%d\n", s);
}
void sub(int argc, char **argv){
int i = 1, s = 0, x = 0;
if(isHex(argv[i], strlen(argv[i])))
s = (x =convertHex(argv[i], strlen(argv[i])));
else
s = atoi(argv[i]);
printf("%s - ", argv[i]);
for(i = 2;i<argc;i++){
if(isHex(argv[i], strlen(argv[i]))){
s -= (x = convertHex(argv[i], strlen(argv[i])));
printf("%s", argv[i]);
}
else{
printf("%s", argv[i]);
s -= atoi(argv[i]);
}
if(i == argc-1)
printf(" = ");
else
printf(" - ");
}
printf("%d\n", s);
}
int main(int argc,char** argv){
char *choices[] = {"Exit", "Addition", "Subtraction"};
void (*calc[])(int, char**) = {NULL, add, sub};
if(argc < 3)
fprintf(stderr, "You must enter at least two arguments.\n");
else if(argc > 16)
fprintf(stderr, "You cannot enter more than 15 arguments.\n");
else{
int ch=1;
while(ch!=0){
menu(choices);
scanf("%d",&ch);
switch(ch){
case 0:
exit(0);
break;
case 1:
calc[1](argc,argv);
break;
case 2:
calc[2](argc,argv);
break;
default:
printf("Enter valid option\n");
break;
}
}
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
The relevant functions are add/sub. It gets tricky because of needing to output the operations to the user. My initial inclination was to go with something like an op-code 1 = add, 2 = sub, but I don't know if that just adds more if-checks in one function and ultimately makes it as long or harder to read.
EDIT: removed alternative implementation that wasn't much better.
Does anyone have any suggestions or helpful advice for reducing the redundancy with add/sub (if its even possible)? Its not entirely necessary, but I'd appreciate any assistance.
EDIT: Updated semi-improved code, now with more functionality:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define maxItems 15
typedef struct _Menu {
int count;
char *items[maxItems];
} Menu;
char *choices[] = {"Exit", "Addition", "Subtraction", "Multiplication", "Division", "Modulo", "Reverse Input"};
int count = sizeof(choices)/sizeof(choices[0]);
int printMenu(const Menu *menu){
int ch;
printf("\n");
for (int i=0; i<menu->count; i++)
printf("%d. %s\n", i, menu->items[i]);
printf("\nMenu Item: ");
scanf("%d",&ch);
return ch;
}
void strrev(char *str){
if (str == 0)
return;
if (*str == 0)
return;
/* get range */
char *start = str;
char *end = start + strlen(str) - 1; /* -1 for \0 */
char temp;
/* reverse */
while (end > start){
/* swap */
temp = *start;
*start = *end;
*end = temp;
/* move */
++start;
--end;
}
}
void rev(int argc, char **argv){
for (int i = argc-1; i > 0; --i){
strrev(argv[i]);
printf("%s ", argv[i]);
}
printf("\n");
}
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
int mult(int a, int b){
return a*b;
}
float divi(int a, int b){
return a/b;
}
int mod(int a, int b){
return a%b;
}
int applyOperator(int argc, char **argv, int (*op)(int, int)) {
int retval = strtol(argv[0], NULL, 0);
for (int i = 1; i < argc; ++i) {
retval = op(retval, strtol(argv[i], NULL, 0));
}
return retval;
}
void printList(int argc, char **argv, const char *sep) {
printf("%s", argv[1]);
for (int i = 2; i < argc; ++i) {
printf("%s%s", sep, argv[i]);
}
}
//Attempting implementation of function dispatch table
int (*calc[])(int, int) = {add, sub, mult};
int main(int argc,char** argv){
Menu menu;
menu.count = min(count, maxItems);
for (int i=0; i < menu.count; i++)
menu.items[i] = choices[i];
if(argc < 3)
fprintf(stderr, "You must enter at least two arguments.\n");
else if(argc > 16)
fprintf(stderr, "You cannot enter more than 15 arguments.\n");
else{
int ch=1;
while(ch!=0){
ch = printMenu(&menu);
switch(ch){
case 0:
exit(0);
break;
case 1:
printList(argc, argv, " + ");
printf(" = %d\n", applyOperator(argc, argv, calc[0]));
break;
case 2:
printList(argc, argv, " - ");
printf(" = %d\n", applyOperator(argc, argv, calc[1]));
break;
case 3:
printList(argc, argv, " * ");
printf(" = %d\n", applyOperator(argc, argv, calc[2]));
break;
case 4: //Divides just the first two args.
printf("%s / %s", argv[1], argv[2]);
printf(" = %f\n", divi(strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0)));
break;
break;
case 5: //Mods just the first two args.
printf("%s mod %s", argv[1], argv[2]);
printf(" = %d\n", mod(strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0)));
break;
case 6:
rev(argc,argv);
break;
default:
printf("Enter valid option\n");
break;
}
}
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
```