Mapping string to enum value

Viewed 803

In my program, I have an enum that is used for indexing my array members. The reason is that it is much easier for me to understand which parameter I am accessing without knowing its index in the array

enum param_enum
{
    AA,
    AB,
    AC,
    AD,
    PARAM_COUNT
};


static int16_t parameters[PARAM_COUNT] =
{
    [AA] = 5, 
    [AB] = 3, 
    [AC] = 4,
    [AD] = 8,
};

I can then access any parameter for example:

parameters[AA] = 10; // Update AA parameter to value 10.

I will be receiving serial commands such as :

"AA:15"

When I receive this command, I must determine what parameter I need to modify based on the first 2 characters, then skip the 3rd character( because it is just ":" and I dont care about it) and the remaining characters will show the new value)

I wonder if there is any easier way to map the enum to a string

My current method:

// line holds the string data
// cmd_size is the length of string data
bool parse_command(char *line, uint16_t cmd_size)
{
printf("data size = %u \n",cmd_size);
char temp_buf[3] = {0};
temp_buf[0] = line[0];
temp_buf[1] = line[1];
printf("temp_buf = %s \n",temp_buf);
if (!strcmp("aa", temp_buf))
    {
        printf("aa: detected \n");
        char temp_storage[5];
        int16_t final_value;
        for(int i = 3;i<=cmd_size; i++){
                temp_storage[i-3]=line[i]; // read data and append to temp bufferfrom the 3rd character till the end of line
                if(line[i] == 0){
                    printf("null termination triggered \n");
                    final_value = strtol(temp_storage,NULL,10); // convert char array to int16_t
                    printf("temp var = %i \n",final_value);             
                }
        }

        return true;
    }

}

The above method seems to work fine but I do not believe that this is the most appropriate solution for this particular task.

3 Answers

If you don't mind what the actual values of the enumeration constants are, you could define those values to be equivalent to the first two characters of the test string. You can then copy those first two characters into a variable of that enum type, which will then adopt the appropriate enumeration directly.

You can define the values using two-character integer literals (like 'BA'). On little-endian systems (such as Windows), the two characters would be in reverse order; for big-endian systems, they would be in direct order.

Here's an example little-endian implementation:

#include <stdio.h>
#include <string.h>

enum param_enum {
    // Reverse byte order from strings for little-endian; keep "as-is" for big-endian...
    AA = 'AA',
    AB = 'BA',
    AC = 'CA',
    AD = 'DA'
};

int main(void)
{
    char test[100];
    while (1) {
        printf("Enter test string (Q to quit): ");
        if (scanf("%99s", test) != 1 || strcmp(test, "Q") == 0) break;
        enum param_enum penum;
        memset(&penum, 0, sizeof(penum));   // To clear any 'upper' bytes
        memcpy(&penum, test, 2);            // Now copy the first 2 byte2
        switch (penum) {
            case AA:
                printf("Code is AA.\n");
                break;
            case AB:
                printf("Code is AB.\n");
                break;
            case AC:
                printf("Code is AC.\n");
                break;
            case AD:
                printf("Code is AD.\n");
                break;
            default:
                printf("Unknown code.\n");
                break;
        }
    }
    return 0;
}

If your compiler doesn't support multicharacter literals (such support is optional, according to the C Standard, IIRC), you can specify equivalent values using hexadecimal constants and the characters' ASCII codes (assuming your platforms uses ASCII encoding), instead:

enum param_enum {
    AA = 0x4141, // 'AA'
    AB = 0x4241, // 'BA'
    AC = 0x4341, // 'CA'
    AD = 0x4441  // 'DA'
};

You could use a look-up table of strings indexed by the param enum, and a function to look up the param enum from the string:

enum param_enum {
    AA,
    AB,
    AC,
    AD
};

static const char * const param_prefix[] = {
    [AA] = "AA",
    [AB] = "AB",
    [AC] = "AC",
    [AD] = "AD",
};

#define ARRAYLEN(a) (sizeof (a) / sizeof (a)[0])

int find_param(const char *value, size_t value_len) {
   int i;
   const char *colon = memchr(value, ':', value_len);

   if (!colon) {
       /* not found */
       return -1;
   }

   /* use length up to colon */
   value_len = colon - value;

   for (i = 0; i < ARRAYLEN(param_prefix); i++) {
       if (param_prefix[i]) {
           size_t prefix_len = strlen(param_prefix[i]);

           if (value_len == prefix_len &&
               memcmp(param_prefix[i], value, prefix_len) == 0) {
               /* found */
               return i;
           }
       }
   }
   /* not found */
   return -1;
}

Example usage:

        // (using 3 for length here, but should use something better)
        int penum = find_param((char*)param->write.value, 3);
        if(penum >= 0) {
            printf("%s parameter need to change\n", param_prefix[penum]);
        }

You could use X Macro technique.

#define PARAM_XMACRO \
    X(AA) \
    X(AB) \
    X(AC) \
    X(AD)


enum param_enum{
#define X(NAME) NAME,
PARAM_XMACRO
#undef X
};

int process() {
    ...
    char *val = (char*)param->write.value;
#define X(NAME) \
    if (strcmp(val, #NAME ":") == 0) {                   \
            printf(#NAME " parameter need to change\n"); \
            return NAME;                                 \
    }
PARAM_XMACRO
#undef X
    return -1;
}

It will expand as: (newlines added for clarity)

enum param_enum{
AA, AB, AC, AD,
};

int process() {
    ...
    char *val = (char*)param->write.value;

if (strcmp(val, "AA" ":") == 0) {
   printf("AA" " parameter need to change\n");
   return AA;
}
if (strcmp(val, "AB" ":") == 0) {
  printf("AB" " parameter need to change\n");
  return AB;
}
if (strcmp(val, "AC" ":") == 0) {
  printf("AC" " parameter need to change\n");
  return AC;
}
if (strcmp(val, "AD" ":") == 0) {
  printf("AD" " parameter need to change\n");
  return AD;
}
  return -1;
}
Related