Just for completeness, here is a live example. The key is to use the type tag enum to select the appropriate functions for each info type (like the following print functions) which would be virtual members in C++:
enum SysInfoE { S, P }; // one for each type// print a SysInfo_Svoid print_S(void* vp){ SysInfo_S* sp = (SysInfo_S *)vp; printf("some int: %d, ", sp->some_S_int); for (unsigned int i = 0; i < sizeof sp->some_S_char; i++) { printf("char[%u]: %d, ", i, sp->some_S_char[i]); }}// print a SysInfo_Pvoid print_P(void* vp){ SysInfo_P* pp = (SysInfo_P*)vp; printf("some int: %d, ", pp->some_P_int); for (unsigned int i = 0; i < sizeof pp->some_P_float / sizeof *pp->some_P_float; i++) { printf("float[%u]: %f, ", i, pp->some_P_float[i]); }}// A global array of function pointers. Index with S or P.void (*pr_func[])(void*) = { print_S, print_P };void print_dev(Device* dp){ printf("dev type: %d\n", dp->InfType); pr_func[dp->InfType](&dp->sys_info); // use proper function in array, pass ptr to union}
This "infrastructure" would be in a different translation unit, possibly in a library. The calling code is in a different file and looks like this:
int main(){ char s_data[] = "123456789"; // plus 0 byte const float p_data[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; Device devices[2] = { { S }, { P } }; memcpy(devices[0].sys_info.s.some_S_char, s_data, sizeof devices[0].sys_info.s.some_S_char); memcpy(devices[1].sys_info.p.some_P_float, p_data, sizeof devices[1].sys_info.p.some_P_float / sizeof *devices[1].sys_info.p.some_P_float); for( unsigned int i= 0; i < sizeof devices / sizeof *devices; i++) { // uniform handling, code does not need to know different sysinfo types print_dev(devices+i); printf("\n"); }}