brianh@hpcvia.CV.HP.COM (brian_helterline) (03/07/90)
I have a question about the following code. Given a structure like: struct FOO { int type_of_data; int number_of_elements; union { int *int_ptr; unsigned int *un_int_ptr; long *long_ptr; } ptr; }; And a fuction to sum up the elements: double sum( struct FOO foo ) { int i; element_size; long sum = 0L; char *data_ptr; switch ( foo.type_of_data ) { case 0 : /* integer data */ data_ptr = (char *) foo.ptr.int_ptr; element_size = sizeof( int ); break; case 1 : /* unsigned data */ data_ptr = (char *) foo.ptr.un_int_ptr; element_size = sizeof( unsigned int ); break; case 2 : /* long data */ data_ptr = (char *) foo.ptr.long_ptr; element_size = sizeof( long ); break; default : abort_with_error(); } for( i=0; i<foo.number_of_elements; i++) { sum += (double) *data_ptr; data_ptr += element_size; } return( sum ); } Here are my questions: 1) Is the above code ok? (other that typos which I've missed.) 2) Is this the best way to handle different types of pointers? I realize that the above code depends on sizof( char ) == 1. What is a better way? I am stuck with the struct *AS IS*. Thanks for any/all help/advice
steve@wattres.UUCP (Steve Watt) (03/08/90)
In article <31530004@hpcvia.CV.HP.COM> brianh@hpcvia.CV.HP.COM (brian_helterline) writes: +I have a question about the following code. Given a structure like: + +struct FOO { + int type_of_data; + int number_of_elements; + union { + int *int_ptr; + unsigned int *un_int_ptr; + long *long_ptr; + } ptr; + }; + +And a fuction to sum up the elements: + +double sum( struct FOO foo ) +{ [somewhat long broken code omitted, see original article] +} +Here are my questions: + +1) Is the above code ok? (other that typos which I've missed.) No. When a pointer to char is dereferenced, you get a char. ONLY. The summing routine needs to be somewhat uglier... Either the switch statement needs to be inside the summing loop, or create a function that returns double, given a pointer to the data type, and create an array of them. typedef enum { int_data, unsigned_data, long_data } data_type; struct FOO { data_type type_of_data; int number_of_elements; int size_of_element; /* In case you want to get some other weird type */ /* Set to sizeof(data_type) */ union { /* These are not really needed for this set of routines... int *int_ptr; unsigned int *un_int_ptr; long *long_ptr; */ void *void_ptr; } ptr; }; double iptod(void *), uptod(void *), lptod(void *); double (*funcs[])(void *) = { iptod, uptod, lptod }; double sum( struct FOO foo ) { /* Cogito ergo sum? :) */ int i; void *p = foo.ptr.void_ptr; double tmp = 0.0; for (i = 0; i < foo.number_of_elements; i++) { tmp += funcs[foo.type_of_data](p); /* see note below */ p = (char *)p + foo.size_of_element; } /* Non-ANSI compilers would require the noted line to be something like: tmp += (*funcs[foo.type_of_data])(p); Some people may also prefer this. */ return (tmp); } double iptod( void *p ) { return (double) (*(int *)p) ; } double uptod( void *p ) { return (double) (*(unsigned *)p) ; } double lptod( void *p ) { return (double) (*(long *)p) ; } Notice that this code is easy to extend to a new data type: If you need to support snorkelwackers, then change the enum to be: int_data, unsigned_data, long_data, snorkelwacker_data add this prototype: double snorkelwackerptod(void *); /* In the prototypes */ add snorkelwackerptod to the function array, so it looks like iptod, uptod, lptod, snorkelwackerptod and double snorkelwackerptod( void *p ) { return (double) (*((snorkelwacker *)p)->snkwck_data_1); } Tah dah! You can now sum up snorkelwacker data, as well! +2) Is this the best way to handle different types of pointers? I realize + that the above code depends on sizof( char ) == 1. What is a + better way? I am stuck with the struct *AS IS*. The best way is to use C++ :). By the way: sizeof(char) is defined to be 1. +Thanks for any/all help/advice No problem! -- Steve Watt ...!claris!wattres!steve wattres!steve@claris.com also works If you torture your data long enough, it'll eventually confess.
gary@hpavla.AVO.HP.COM (Gary Jackoway) (03/08/90)
> / hpavla:comp.lang.c / brianh@hpcvia.CV.HP.COM (brian_helterline) I have a question about the following code. Given a structure like: > > struct FOO { > int type_of_data; > int number_of_elements; > union { > int *int_ptr; > unsigned int *un_int_ptr; > long *long_ptr; > } ptr; > }; >... > case 2 : /* long data */ > data_ptr = (char *) foo.ptr.long_ptr; > element_size = sizeof( long ); > break; > > for( i=0; i<foo.number_of_elements; i++) > { > sum += (double) *data_ptr; > data_ptr += element_size; > } > 1) Is the above code ok? (other that typos which I've missed.) No, this will not work. The problem is that data_ptr is a char *. That means when you say (double) *data_ptr, you will double the byte length value at the location data_ptr points to. So whether you had an int, unsigned int or long, you would only sum the first byte of each data field. Instead you will need to have some caseing mechanism within the loop. Personally I'd say: switch (foo.type_of_data) case 0: for (i=0, iptr = foo.ptr.int_ptr; i<foo.number_of_elements; ++i, ++iptr) sum += *iptr; break; case 1: ... and do this for each type of data with different pointers. Note that iptr must be sized correctly (int *iptr). Then ++iptr is sufficient without going through any element size contortions. Gary Jackoway