[comp.lang.c] Different Pointer Types

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