2

Is there a way of knowing the type of a struct member at compile time? Something analogous to offsetof(), but for types.

Eg. something like:

typedef struct{
  int  b;
  char c;
}a_t;

typeof(a_t,b) a_get_b(void* data){
  return *(typeof(a_t,b)*)(data + offsetof(a_t,b));
}
étale-cohomology
  • 2,098
  • 2
  • 28
  • 33
  • 2
    `data + offsetof(a_t,b)` --> Adding an offset to a `void *` is UB. – chux - Reinstate Monica Mar 28 '22 at 21:26
  • @chux-ReinstateMonica You sure? I just assumed it was always 1 byte per int – étale-cohomology Mar 28 '22 at 21:27
  • Sorry, I accidentally deleted my comment! What it said was, if you have C11 then https://stackoverflow.com/a/28897994/2193968 may help. – Jerry Jeremiah Mar 28 '22 at 21:29
  • 1
    étale-cohomology Yes - sure: [C null pointer arithmetic](https://stackoverflow.com/a/54233071/2410359) – chux - Reinstate Monica Mar 29 '22 at 00:02
  • To remind: A quick note about C and `offsetof`: Per C if "a null pointer is guaranteed to compare unequal to a pointer to any object" and "any two null pointers shall compare equal", then a null pointer is _not_ a pointer to object. Hence, "a postfix expression followed by the `->` operator and an identifier" does _not_ designate a member of a structure object. Hence, [`((st *)0)->m`](https://en.wikipedia.org/wiki/Offsetof) violates the semantics of the `->` operator. However, if the `offsetof` was tested, then it _can_ be used. Taken from: https://stackoverflow.com/a/47499126/1778275. – pmor Mar 30 '22 at 21:53

3 Answers3

2

If you're willing to use typeof (which is currently a very common nonstandard C extension slated for inclusion in the next version of the standard), you can apply it to a member obtained from a compound literal as in typeof((a_t){0}.b):

typedef struct{ int  b; char c; }a_t;

typeof((a_t){0}.b) a_get_b(void* data){ return (a_t*){data}->b; }

(Given a type a_t, (a_t){0} is a reliable way to get an instance of it. Because of how initialization works in C, the 0 will initialize a deepest first elementary member and elementary types are scalars and therefore 0-initializable.)

As for the obtaining the member from a void pointer pointing to the container, you could do:

*(typeof(&(a_t){0}.b)((char*)data + offsetof(a_t,b))

but that's just an awfully long-winded way to do:

(a_t*){data}->b

(which is 100% equivalent to the former as long as the effective type of data is indeed a_t*).

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Thank you, this exactly the answer I was looking for. (Posting again because someone deleted the previous comment? Who would delete a "thank you" comment?) – étale-cohomology Mar 31 '22 at 00:30
1

Another way it works:

#include <stdio.h>

#define typeof_element(_struct,el) typeof(((_struct *)(0))->el)

typedef struct{
    int a;
    int b;
}Row;

int main()
{
   typeof_element(Row, a) value_a = 10;
    
   printf("%d\n", value_a);
   
   return 0;
}
0

Another way (other than Jerry Jeremiah's) is:

#define struct_get(STRUCT,ELEM) *(typeof(STRUCT.ELEM)*) (STRUCT+offsetof(typeof(STRUCT),ELEM))
étale-cohomology
  • 2,098
  • 2
  • 28
  • 33