Accessing a struct field using an int or enum value

Viewed 129

I currently use a logging system that uses a tag value to identify the parameter that it will store. The format we are using is the following: Tag + Time + value.

The next step is to take a logged message and send it to a server using Protocol Buffers to serialize the data.

The protocol buffer .proto file, provides a struct with all the fields each corresponding to a tag in the original logging system.

The problem comes when I need to efficiently read the tag and assign the value to a field in the protocol buffer struct. In essence I would for example like to take in tag 5 and be able to find the field 5 in the struct and write the value.

I know this could be done with switch cases, but we are using around 50 tags so I would like to avoid this solution if possible. I attach an example struct to illustrate the problem.

/* Struct definitions */
typedef struct _Profiles {
    int32_t param1;
    int32_t param2;
    int32_t param3;
    int32_t param4;
    int32_t param5;
    int16_t param6;
    int32_t param7;
    uint32_t param8;
    int32_t param9;
    int32_t param10;
    uint32_t param11;
    int32_t time;
/* @@protoc_insertion_point(struct:Profiles) */
} Profiles;

The expected result would be that I can store a logged line like the following: 5 1345643 1500 (tag, time, value)

to a protocol buffer struct:

profiles.param5 = 1500
profiles.time  = 1345643 

without the need of 12 switch cases (in this example). Basically, how can I access the 5th declared field of a struct using an integer/enum.

Bare in mind that each field of the struct could potentially have a different type.

2 Answers

My approach would be having pointers to each of the fields as below.

int *ptr[11] = {&profiles.param1, &profiles.param2,  &profiles.param3,,,,,, &profiles.param11};

And when the message arrives, I will update the field using ptr.

  *ptr[tag-1] = 1500; //tag-1 because ptr[4] points to profiles.param5

Improving on the previous solution by @kiran.

In case you have multiple types then you need to store void pointers. But for typecasting you again need to have a switch case.

This can be avoided in two ways.

  1. Store the type along with the data. i.e, store (tag, type, time, value). Then you can have a limited no of conditions for the types.

    void *ptr[11] = {&profiles.param1, &profiles.param2,  &profiles.param3,,,,,, &profiles.param11};
    
    switch (type)
    {
        case 0:
            *(char*)ptr[tag-1] = value;
            break;
        case 1:
            *(int*)ptr[tag-1] = value;
            break;
        ...
    }
    
  2. Alternatively you can have a map which stores the type of the value

    void *ptr[11] = {&profiles.param1, &profiles.param2,  &profiles.param3,,,,,, &profiles.param11};
    char typearr[11] = {0,1,0,0....1};  // 0 for char, 1 for int etc
    
    type = typearr[tag-1];
    
    switch (type)
    {
       ...
    } 
    
Related