The purpose of the following example is to how to use enumerations in a code that’s already familiar from Programming 1. There are three enumeration types declared:
- Specifying command line argument position
- Encoding error codes
- Encoding mathematical operations
In addition, there are a few “clever tricks” shown that have not been in the lab materials up to this point.
- Declaring an array of error texts and using enum values to declare and index them.
- Creating a structure array that contains both text and coded enum value pairs.
The example is provided to you in two variations
- The entire example is written in one code file. This is suitable to look at on week #2, when enum types are introduced
- The example divided into a code and header file. This is suitable for week #4. This is also very close to the minimum requirements of homework 1.
|
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
/** * File: calc_enum.c * Author: Risto Heinsar * Created: 25.01.2026 * Edited: 26.01.2026 * * Description: Example program that's based on Programming 1 calculator lab. * Introduces simple use cases for enum type. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #define DEBUG 0 #define ARGS_CNT_EXPECTED 4 enum ArgsPosition { ARGS_EXEC, ARGS_OPERAND_1, ARGS_OPERATION, ARGS_OPERAND_2 }; enum MathOperation { OP_ADD, OP_SUB, OP_MULT, OP_DIV, OP_UNKNOWN }; enum ErrorCode { ERR_ARG_CNT = 0, ERR_UNRECOGNIZED_ARG, ERR_OPERATION_INVALID, ERR_OPERAND_MULT_SEPARATOR, ERR_OPERAND_UNKNOWN_SYMBOL, ERR_DIV_ZERO }; struct MathOperationEncoding { char opCode; // opcodes as '+' etc enum MathOperation operation; // opcodes as OP_ADD etc }; const struct MathOperationEncoding mathOperations[] = {{'+', OP_ADD}, {'-', OP_SUB}, {'*', OP_MULT}, {'x', OP_MULT}, {'/', OP_DIV }}; double Calculate(double val1, double val2, enum MathOperation op); void ErrorHandler(enum ErrorCode code); void DebugArgs(int argc, char **argv); double EvalOperand(const char *operand); enum MathOperation GetOperation(const char *op); void Usage(); void CheckArgs(int argc, char **argv); // Contains argv[0] to print exec path in Usage() for guidance const char *execName; int main(int argc, char **argv) { execName = argv[ARGS_EXEC]; CheckArgs(argc, argv); if (DEBUG) DebugArgs(argc, argv); double val1 = EvalOperand(argv[ARGS_OPERAND_1]); double val2 = EvalOperand(argv[ARGS_OPERAND_2]); enum MathOperation op = GetOperation(argv[ARGS_OPERATION]); printf("> %.2f\n", Calculate(val1, val2, op)); return EXIT_SUCCESS; } /** * Description: Calculates the mathematical equation and returns the answer * * Parameters: val1 - first operand * val2 - second operand * op - mathematical operation coded value * * Return: result of the mathematical operation */ double Calculate(double val1, double val2, enum MathOperation op) { // Note: all cases either return or exit using ErrorHandler() switch (op) { case OP_ADD: return val1 + val2; case OP_SUB: return val1 - val2; case OP_MULT: return val1 * val2; case OP_DIV: if (val2 == 0) ErrorHandler(ERR_DIV_ZERO); return val1 / val2; default: ErrorHandler(ERR_OPERATION_INVALID); } // This should never be reached return 0.0; } /** * Description: Checks if the operation passed is supported and decodes it to * enum value * * Parameters: op - mathematical operation as a string, to be checked * * Return: enum value representing the decoded mathematical operation */ enum MathOperation GetOperation(const char *op) { // Safety check for empty arg, support single character operations only if (op[0] == '\0' || op[1] != '\0') ErrorHandler(ERR_OPERATION_INVALID); // Grab the character representing the operation char opCode = op[0]; // Compare against the list of known operations size_t opCount = sizeof(mathOperations) / sizeof(mathOperations[0]); for (size_t i = 0; i < opCount; i++) { if (opCode == mathOperations[i].opCode) return mathOperations[i].operation; } // No operation codes matched return OP_UNKNOWN; } /** * Description: Checks if the operand is a decimal number, converts it to * a double and returns it * * Parameters: operand - operand being checked as string * * Return: operand converted to double */ double EvalOperand(const char *operand) { // Used to keep track of the char currently being checked for validity const char *p = operand; // Skip first char if it's a sign if (*p == '-' || *p == '+') p++; int commaCount = 0; while (*p) { if (*p == '.') { commaCount++; if (commaCount > 1) ErrorHandler(ERR_OPERAND_MULT_SEPARATOR); } // Checks 0 .. 9 if (!isdigit((unsigned char)*p)) ErrorHandler(ERR_OPERAND_UNKNOWN_SYMBOL); // Move to next character p++; } return atof(operand); } /** * Description: Prints error message, usage help and terminates the program * * Parameters: code - error code that triggered exit condition * * Return: - */ void ErrorHandler(enum ErrorCode code) { const char *errorPrintableText[] = { [ERR_ARG_CNT] = "Invalid argument count", [ERR_UNRECOGNIZED_ARG] = "Unrecognized argument", [ERR_OPERATION_INVALID] = "Invalid operation", [ERR_OPERAND_MULT_SEPARATOR] = "Invalid operand, unexpected decimal separator", [ERR_OPERAND_UNKNOWN_SYMBOL] = "Invalid operand, not a number", [ERR_DIV_ZERO] = "Dividing by zero" }; fprintf(stderr, "Error %d: ", code); fprintf(stderr, "%s\n", errorPrintableText[code]); Usage(); exit(EXIT_FAILURE); } /** * Description: Prints program usage information * * Parameters: - * * Return: - */ void Usage() { printf("\nUsage: %s operand operation operand\n\n", execName); printf("Possible operations: \n"); printf("\t+ add\n"); printf("\t- subtract\n"); printf("\t* multiply\n"); printf("\t/ divide\n"); printf("\nNote, that due to how shell works, \n" "the multiplication operator has to be encased in quotes\n" "e.g. 2 \"*\" 3.5\n"); } /** * Description: Checks for the help argument, triggers usage help * Checks if all arguments required for calculation are present * * Parameters: argc - number of command line arguments * argv - arguments that were passed * * Return: - */ void CheckArgs(int argc, char **argv) { // Check for help argument if (argc == 2) { if (!strcmp(argv[1], "--help")) { Usage(); exit(EXIT_SUCCESS); } ErrorHandler(ERR_UNRECOGNIZED_ARG); } // Check expected arg count for calculator if (argc != ARGS_CNT_EXPECTED) ErrorHandler(ERR_ARG_CNT); } /** * Description: Prints out all command line arguments, for debugging purposes * * Parameters: argc - number of command line arguments * argv - arguments that were passed * * Return: - */ void DebugArgs(int argc, char **argv) { printf("Total arguments: %d\n", argc); for (int i = 0; i < argc; i++) { printf("argv[%d] = %s\n", i, argv[i]); } } |
The following example contains two files. Both must be placed in the same directory. The name of the header is dependent on the #include statement in calc_enum.h . It is a common practice to keep the header and source file name the same, but with a different extension.
The following has been transferred from the code file to the header file:
- Comments that are directed for developers using the functions
- Macros
- Structure and enumeration declarations
- Function prototypes (declarations)
Additionally, short comments intended for developers of that file were added to the code file and header guard was added to the header file to avoid multiple conflicting declarations.
|
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
/** * File: calc_enum.h * Author: Risto Heinsar * Created: 25.01.2026 * Edited: 26.01.2026 * * Description: Example program that's based on Programming 1 calculator lab. * Introduces simple use cases for enum type. */ #ifndef CALC_ENUM_H #define CALC_ENUM_H #define DEBUG 0 #define ARGS_CNT_EXPECTED 4 enum ArgsPosition { ARGS_EXEC, ARGS_OPERAND_1, ARGS_OPERATION, ARGS_OPERAND_2 }; enum MathOperation { OP_ADD, OP_SUB, OP_MULT, OP_DIV, OP_UNKNOWN }; enum ErrorCode { ERR_ARG_CNT = 0, ERR_UNRECOGNIZED_ARG, ERR_OPERATION_INVALID, ERR_OPERAND_MULT_SEPARATOR, ERR_OPERAND_UNKNOWN_SYMBOL, ERR_DIV_ZERO }; struct MathOperationEncoding { char opCode; // opcodes as '+' etc enum MathOperation operation; // opcodes as OP_ADD etc }; /** * Description: Calculates the mathematical equation and returns the answer * * Parameters: val1 - first operand * val2 - second operand * op - mathematical operation coded value * * Return: result of the mathematical operation */ double Calculate(double val1, double val2, enum MathOperation op); /** * Description: Checks if the operation passed is supported and decodes it to * enum value * * Parameters: op - mathematical operation as a string, to be checked * * Return: enum value representing the decoded mathematical operation */ void ErrorHandler(enum ErrorCode code); /** * Description: Checks if the operand is a decimal number, converts it to * a double and returns it * * Parameters: operand - operand being checked as string * * Return: operand converted to double */ void DebugArgs(int argc, char **argv); /** * Description: Prints error message, usage help and terminates the program * * Parameters: code - error code that triggered exit condition * * Return: - */ double EvalOperand(const char *operand); /** * Description: Prints program usage information * * Parameters: - * * Return: - */ enum MathOperation GetOperation(const char *op); /** * Description: Checks for the help argument, triggers usage help * Checks if all arguments required for calculation are present * * Parameters: argc - number of command line arguments * argv - arguments that were passed * * Return: - */ void Usage(); /** * Description: Prints out all command line arguments, for debugging purposes * * Parameters: argc - number of command line arguments * argv - arguments that were passed * * Return: - */ void CheckArgs(int argc, char **argv); #endif |
|
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
/** * File: calc_enum.c * Author: Risto Heinsar * Created: 25.01.2026 * Edited: 26.01.2026 * * Description: Example program that's based on Programming 1 calculator lab. * Introduces simple use cases for enum type. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include "calc_enum.h" const struct MathOperationEncoding mathOperations[] = {{'+', OP_ADD}, {'-', OP_SUB}, {'*', OP_MULT}, {'x', OP_MULT}, {'/', OP_DIV}}; // Contains argv[0] to print exec path in Usage() for guidance const char *execName; int main(int argc, char **argv) { execName = argv[ARGS_EXEC]; CheckArgs(argc, argv); if (DEBUG) DebugArgs(argc, argv); double val1 = EvalOperand(argv[ARGS_OPERAND_1]); double val2 = EvalOperand(argv[ARGS_OPERAND_2]); enum MathOperation op = GetOperation(argv[ARGS_OPERATION]); printf("> %.2f\n", Calculate(val1, val2, op)); return EXIT_SUCCESS; } /** * Calculates the result, can cause exit on errors */ double Calculate(double val1, double val2, enum MathOperation op) { // Note: all cases either return or exit using ErrorHandler() switch (op) { case OP_ADD: return val1 + val2; case OP_SUB: return val1 - val2; case OP_MULT: return val1 * val2; case OP_DIV: if (val2 == 0) ErrorHandler(ERR_DIV_ZERO); return val1 / val2; default: ErrorHandler(ERR_OPERATION_INVALID); } // This should never be reached return 0.0; } /** * Checks the operation symbol, can throw errors that will exit */ enum MathOperation GetOperation(const char *op) { // Safety check for empty arg, support single character operations only if (op[0] == '\0' || op[1] != '\0') ErrorHandler(ERR_OPERATION_INVALID); // Grab the character representing the operation char opCode = op[0]; // Compare against the list of known operations size_t opCount = sizeof(mathOperations) / sizeof(mathOperations[0]); for (size_t i = 0; i < opCount; i++) { if (opCode == mathOperations[i].opCode) return mathOperations[i].operation; } // No operation codes matched return OP_UNKNOWN; } /** * Checks validity of the numeric string, can throw errors that will exit */ double EvalOperand(const char *operand) { // Used to keep track of the char currently being checked for validity const char *p = operand; // Skip first char if it's a sign if (*p == '-' || *p == '+') p++; int commaCount = 0; while (*p) { if (*p == '.') { commaCount++; if (commaCount > 1) ErrorHandler(ERR_OPERAND_MULT_SEPARATOR); } // Checks 0 .. 9 if (!isdigit((unsigned char)*p)) ErrorHandler(ERR_OPERAND_UNKNOWN_SYMBOL); // Move to next character p++; } return atof(operand); } /** * Prints error text and terminates the program */ void ErrorHandler(enum ErrorCode code) { const char *errorPrintableText[] = { [ERR_ARG_CNT] = "Invalid argument count", [ERR_UNRECOGNIZED_ARG] = "Unrecognized argument", [ERR_OPERATION_INVALID] = "Invalid operation", [ERR_OPERAND_MULT_SEPARATOR] = "Invalid operand, unexpected decimal separator", [ERR_OPERAND_UNKNOWN_SYMBOL] = "Invalid operand, not a number", [ERR_DIV_ZERO] = "Dividing by zero" }; fprintf(stderr, "Error %d: ", code); fprintf(stderr, "%s\n", errorPrintableText[code]); Usage(); exit(EXIT_FAILURE); } /** * Prints usage guide */ void Usage() { printf("\nUsage: %s operand operation operand\n\n", execName); printf("Possible operations: \n"); printf("\t+ add\n"); printf("\t- subtract\n"); printf("\t* multiply\n"); printf("\t/ divide\n"); printf("\nNote, that due to how shell works, \n" "the multiplication operator has to be encased in quotes\n" "e.g. 2 \"*\" 3.5\n"); } /** * Checks command line arg count and --help, can throw errors to exit */ void CheckArgs(int argc, char **argv) { // Check for help argument if (argc == 2) { if (!strcmp(argv[1], "--help")) { Usage(); exit(EXIT_SUCCESS); } ErrorHandler(ERR_UNRECOGNIZED_ARG); } // Check expected arg count for calculator if (argc != ARGS_CNT_EXPECTED) ErrorHandler(ERR_ARG_CNT); } /** * Prints list of passed command line arguments */ void DebugArgs(int argc, char **argv) { printf("Total arguments: %d\n", argc); for (int i = 0; i < argc; i++) { printf("argv[%d] = %s\n", i, argv[i]); } } |