0

For the problem, consider below two structures.

struct type1_A
{
    int a;
    char b;
    char rsvd[10];
    char c;
    int d;
}

struct type1_B
{
    int d;
    char rsvd[12];
    char c;
    char b;
    int a;
}

I need to read fields a, b, c & d from the structs. I will have a buffer address and that buffer will have one of the struct. A flag can tell what kind of struct it is.

if (flag == TYPE1_A) {
    a = ((struct type1_A*) (buffer))->a;
}
else if (flag == TYPE1_B) {
    a = ((struct type1_B*) (buffer))->a;
}

But when there are many such reads, I dont want to keep on having if-else like above. Is there some way (hack) that this can be done without if-else. The field names will be same but at a different offset.

VinayChoudhary99
  • 846
  • 3
  • 13
  • 26
  • 1
    Instead of multiple `if`...`else`, use one `if`...`else` and in every block cast the buffer address once into a pointer to the selected structure and access *all* fields in one block. Note that there might be padding between the structure fields, and you might get alignment issues when casting the buffer address to a structure pointer. – Bodo Nov 10 '22 at 13:15
  • If `buffer` is a `char *` or similar for data that's read from a file or socket (or anything), code such as `((struct type1_A*) (buffer))` is a [strict aliasing violation](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) and therefore undefined behavior. It can **also** be undefined behavior if whatever you're accessing has stricter alignment requirements than provided by the `char` buffer. – Andrew Henle Nov 10 '22 at 21:10

2 Answers2

0

You can do the pointer arithmetic manually and store the offsets in a table indexed by type. That'd replace the if-else ladder with a table lookup:

if(flag_is_within_type_range(flag)) 
     a=*(int*) ((char*)buffer+offset_table_indexed_by_type[flag]);

Essentially the resulting assembly needs to do a dynamic_type_number-to-an_offset lookup and you having the same overloaded name (i.e., the a member) for those different offsets doesn't help anything. There's hardly any good way to exploit it.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
0

I would use macro like this:

#define TYPE_1 1

#define GETMEMBER(type, buff, field) ((type) == TYPE_1 ? ((struct type1_A *)(buff)) -> field : ((struct type1_B *)(buff)) -> field)

void foo(void *buff)
{
    int type1a = GETMEMBER(TYPE_1, buff, a);
    int type2a = GETMEMBER(0, buff, a);

    printf("Type1: %d, type2:%d\n", type1a, type2a);
}

If you use a constant expression as type the compiler will optimize out the comparison leaving only the assignment:

https://godbolt.org/z/a73YsorMe

0___________
  • 60,014
  • 4
  • 34
  • 74