[comp.lang.c] Passing types to a subroutine

sjr@mimsy.UUCP (Stephen J. Roznowski) (11/10/88)

I'm in the process of developing a piece of code that needs
to be portable across several different machines.  What I
need to do is something of the following:

main()
{
	...

	float	a[100];

	subroutine(...., a[100], ...);
	}

AND

main()
{
	...

	double	a[100];

	subroutine(...., a[100], ...);
	}

and have the subroutine do the right thing with a[].
Eventually, the subroutine needs to so something like:

subroutine(....,array,....)

[???]	*array;
{
	unsign int	i;
	int		scale;
	float		bias;

	...
	array[20] = (cast ???) i * scale + bias;

	}

How do you pass the knowledge of the array type to the
subroutine?  I need to be able to pass the knowledge
that the array a[] is either float or double (or for
that matter unsign int).

Any help will be greatly appreciated.

Stephen J. Roznowski                 sjr@mimsy.umd.edu
-- 
Stephen J. Roznowski                 sjr@mimsy.umd.edu

djones@megatest.UUCP (Dave Jones) (11/10/88)

From article <14457@mimsy.UUCP>, by sjr@mimsy.UUCP (Stephen J. Roznowski):
> I'm in the process of developing a piece of code that needs
> to be portable across several different machines.  ...

[ How to switch between using floats and doubles? ]

If I understand the question, you can do it as follows.  Declare
a typedef for the type "real".  Use "real" everywhere instead of
"float" or "double".

#ifdef FLOATS
typedef float real;
#else 
typedef double real;
#endif

You should be sure you are familiar with the way floats are
cast to doubles on procedure calls, etc..  (See K&R)  If your machine
uses IEEE floating point format, the cast operation takes a nontrivial
amount of time.

chris@mimsy.UUCP (Chris Torek) (11/10/88)

In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>I'm in the process of developing a piece of code that needs
>to be portable across several different machines.  What I
>need to do is something of the following:
[edited]
>	subroutine(...., <float array>, ...);
>	subroutine(...., <double array>, ...);
>
>and have the subroutine do the right thing with a[].

>How do you pass the knowledge of the array type to the
>subroutine?  I need to be able to pass the knowledge
>that the array a[] is either float or double (or for
>that matter unsign int).

This is easy: you cannot do it at all in C.

The best approximation is something like this:

	float a[100];
	subroutine(TYPE_FLOAT, a, (double *)NULL);
	...

	double a[100];
	subroutine(TYPE_DOUBLE, (float *)NULL, a);
	...

	subroutine(t, iffloat, ifdouble)
		enum whichtype t;
		float *iffloat;
		double *ifdouble;
	{
		double x;

		switch (t) {

		case TYPE_FLOAT:
			if (iffloat == NULL || ifdouble != NULL)
				panic("subroutine TYPE_FLOAT");
			break;

		case TYPE_DOUBLE:
			if (iffloat != NULL || ifdouble == NULL)
				panic("subroutine TYPE_DOUBLE");
			break;
		}

		...
		x = t == TYPE_FLOAT ? iffloat[subscript] : ifdouble[subscript];
		...
	}

It should be obvious that the prefix type argument is unnecessary,
being easily computed by which argument is not NULL; in this example
it serves as a double-check.

If the subroutine in question is (or is in) the innermost loop of the
code, it will probably pay to write as many different versions of it as
you have types.  One technique for doing this without actually
duplicating the code (leading to errors of the form `I forgot to update
one') is to use the C preprocessor:

	/*
	 * make sub_float(), which is subroutine() of flavour `float'.
	 */
	#define sub_flavour sub_float
	#define type_flavour float
	#include "subroutine.c"
	#undef sub_flavour
	#undef type_flavour

	/*
	 * make sub_double(), which is the same, but `double'.
	 */
	#define sub_flavour sub_double
	#define type_flavour double
	#include "subroutine.c"
	#undef sub_flavour
	#undef type_flavour

	/*
	 * make sub_unsigned().
	 */
	#define sub_flavour sub_unsigned
	#define type_flavour unsigned
	#include "subroutine.c"
	#undef sub_flavour
	#undef type_flavour

subroutine.c starts out like this:

	sub_flavour(this, that, arr, the_other)
		<types for this, that>
		type_flavour *arr;
		<type for the_other>
	{
		<code>
	}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mesard@bbn.com (Wayne Mesard) (11/10/88)

From article <14461@mimsy.UUCP>, by chris@mimsy.UUCP (Chris Torek):
> In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>>I'm in the process of developing a piece of code that needs
>>to be portable across several different machines.  What I
>>need to do is something of the following:
> [edited]
>>	subroutine(...., <float array>, ...);
>>	subroutine(...., <double array>, ...);
>
> The best approximation is something like this:
> 
> 	float a[100];
> 	subroutine(TYPE_FLOAT, a, (double *)NULL);
> 	...
> 
> 	double a[100];
> 	subroutine(TYPE_DOUBLE, (float *)NULL, a);
> 	...
> 
> 	subroutine(t, iffloat, ifdouble)
> 		enum whichtype t;
> 		float *iffloat;
> 		double *ifdouble;
[Rest deleted.]

Or, how 'bout using a good old union to get rid of the extra param:

   union ptr {
       float *f;
       double *d;
   };

   enum whichtype {TYPE_FLOAT, TYPE_DOUBLE};

   main()
   {
       void sub();
       float f[10];
       double d[10];

       f[1] = 1.234;
       d[1] = -5.432;
       sub(0, f);
       sub(1, d);
       printf("Incidently, the sizeof the union is %d\n", sizeof(union ptr));
   }


   void sub(t, p)
   enum whichtype t;
   union ptr p;
   {
       switch(t) {
	 case TYPE_DOUBLE:
	   printf("Itsa double, cell 1 is %g\n", p.d[1]);
	   break;
	 case TYPE_FLOAT:	
	   printf("Itsa float, cell 1 is %g\n", p.f[1]);
	   break;
       }
   }

-- 
void *Wayne_Mesard();         MESARD@BBN.COM         BBN, Cambridge, MA

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/11/88)

In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>	float	a[100];
>	subroutine(...., a[100], ...);

>	double	a[100];
>	subroutine(...., a[100], ...);

>subroutine(....,array,....)
>[???]	*array;

>How do you pass the knowledge of the array type to the
>subroutine?

You don't.  Types are dealt with at compile time, not run time.
It is in fact possible for a pointer-to-float to be passed
differently than a pointer-to-double.

If you REALLY have to do this, consider casting the arguments
(which should be "a", not "a[100]") to type pointer-to-void or
pointer-to-char, and add another parameter to the subroutine
that can be used to indicate how to "un-cast" the funny
parameter.  The code for this won't be clean..

clayj@microsoft.UUCP (Clay Jackson) (11/11/88)

In article <14461@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>>need to do is something of the following:
>[edited]
>>	subroutine(...., <float array>, ...);
>>	subroutine(...., <double array>, ...);
>>
>>and have the subroutine do the right thing with a[].
>
>This is easy: you cannot do it at all in C.
>
>The best approximation is something like this:
>
(edited for brevity)
>	float a[100];
>	subroutine(TYPE_FLOAT, a, (double *)NULL);
>	...
>
>	double a[100];
>	subroutine(TYPE_DOUBLE, (float *)NULL, a);

Another approach, which I have seen implemented in a commercial product
(NOT made by Microsoft!) is to define a union, ala:

union foo {
  long long_arg;
  short short_arg;
  double double_arg;
 };

Then, reference it in a struct

 struct myargs {
    union foo my_val;
    int arg_type
 };

Then you can even get fancier, and use defines to set up names, like 
#define LONG 0
#define SHORT 1
.
.
and so on, to figure out what the value of arg_type should be.

I first saw this used in "Unify", a database product made by the Unify Corp.
There is nothing, as far as I know, proprietary about it, and it seems like
a pretty elegant solution for something that would otherwise be pretty hard
to do in C.

chris@mimsy.UUCP (Chris Torek) (11/11/88)

In article <32119@bbn.COM> mesard@bbn.com (Wayne Mesard) writes:
>Or, how 'bout using a good old union to get rid of the extra param:

You can do this, but not as coded.  (Original code appears below.)
Instead, you must augment main() with a `union ptr' variable, and
change the calls to

	u.f = f;
	sub(0, u);
	u.p = d;
	sub(1, d);

The code below is not type-correct and WILL NOT WORK on (eg) a Pyramid.
I would also suggest using TYPE_FLOAT and TYPE_DOUBLE rather than the
literal 0 and 1 constants.

- union ptr {
-     float *f;
-     double *d;
- };
-
- enum whichtype {TYPE_FLOAT, TYPE_DOUBLE};
-
- main()
- {
-     void sub();
-     float f[10];
-     double d[10];
-
-     f[1] = 1.234;
-     d[1] = -5.432;
-     sub(0, f);
-     sub(1, d);
-     printf("Incidently, the sizeof the union is %d\n", sizeof(union ptr));
- }
-
-
- void sub(t, p)
- enum whichtype t;
- union ptr p;
[rest deleted]
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

evil@arcturus.UUCP (Wade Guthrie) (11/12/88)

In article <14461@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
> >I'm in the process of developing a piece of code that needs
> >to be portable across several different machines.  What I
> >need to do is something of the following:
> [edited]
> >	subroutine(...., <float array>, ...);
> >	subroutine(...., <double array>, ...);
> >
> This is easy: you cannot do it at all in C.

how about using unions -- something like

union types
{
	float *f;
	double *d;
};

#define FLOAT	1
#define DOUBLE	2

subroutine(...,fred, type);
union *fred;
int type;
{
	. . .
	switch(type)
	{
		case(FLOAT):
			. . . fred->f . . .
			break; 
		case(DOUBLE):
			. . . fred->d . . .
			break; 
		default:
			/* error */
	}
	. . .
}

It's a little cumbersome, but it gets you there -- I used pointers
to floats and doubles because, in most cases, they act like arrays.
I've used things like this and it seems to work okay.


Wade Guthrie
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

henry@utzoo.uucp (Henry Spencer) (11/12/88)

In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>	float	a[100];
>	subroutine(...., a[100], ...);
>
>	double	a[100];
>	subroutine(...., a[100], ...);
>
>and have the subroutine do the right thing with a[].
>How do you pass the knowledge of the array type to the
>subroutine?  I need to be able to pass the knowledge
>that the array a[] is either float or double (or for
>that matter unsign int).

You can't do this in C.  A function parameter has to have a single, specific
type at compile time; you don't get to postpone the decision to run time.
The best you can do is a complicated little dance with unions and pointers,
which isn't really any simpler than just writing three versions of your 
subroutine, one for each type.
-- 
Sendmail is a bug,             |     Henry Spencer at U of Toronto Zoology
not a feature.                 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

karl@haddock.ima.isc.com (Karl Heuer) (11/12/88)

In article <32119@bbn.COM> mesard@bbn.com (Wayne Mesard) writes:
>Or, how 'bout using a good old union to get rid of the extra param:
>   union ptr { float *f; double *d; };
>   main() {
>       float f[10]; double d[10];
>       sub(0, f); sub(1, d);
>   }
>   void sub(t, p) enum whichtype t; union ptr p; { ... }

This is not correct code.  The second parameter is declared to be `union ptr',
but the type actually passed is one of its member types.  This will fail if,
for example, the implementation passes scalars on the stack and structs/unions
with some other mechanism.  To do this `right', you'd have to declare a union
object, copy the value `f' or `d' into its appropriate member, and then pass
the union as the actual argument.

(Also, since you've already gone to the trouble of declaring an enum, you
should probably say `TYPE_FLOAT' or `TYPE_DOUBLE' instead of `0' or `1'.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

sjr@mimsy.UUCP (Stephen J. Roznowski) (11/15/88)

In article <8853@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>>How do you pass the knowledge of the array type to the
>>subroutine?
>
>You don't.  Types are dealt with at compile time, not run time.
>It is in fact possible for a pointer-to-float to be passed
>differently than a pointer-to-double.
>
>If you REALLY have to do this, consider casting the arguments
>(which should be "a", not "a[100]") to type pointer-to-void or
>pointer-to-char, and add another parameter to the subroutine
>that can be used to indicate how to "un-cast" the funny
>parameter.  The code for this won't be clean..

The problem is that I can't determine the array type
at compile time.  These subroutines are going to be part of
a library.

And you are right that the pointers may be different,
or at least have different alignment properties. Is this
true of the pointer-to-void or pointer-to-char?

Stephen

[Let's
make
inews
happy,
by
adding
lines]
[This is worst than the line eater.]
-- 
Stephen J. Roznowski                 sjr@mimsy.umd.edu

sjr@mimsy.UUCP (Stephen J. Roznowski) (11/15/88)

In article <10600@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>In article <32119@bbn.COM> mesard@bbn.com (Wayne Mesard) writes:
>>Or, how 'bout using a good old union to get rid of the extra param:
>>   union ptr { float *f; double *d; };
>>   main() {
>>       float f[10]; double d[10];
>>       sub(0, f); sub(1, d);
>>   }
>>   void sub(t, p) enum whichtype t; union ptr p; { ... }
>
>This is not correct code.  The second parameter is declared to be `union ptr',
>but the type actually passed is one of its member types.  This will fail if,
>for example, the implementation passes scalars on the stack and structs/unions
>with some other mechanism.  To do this `right', you'd have to declare a union
>object, copy the value `f' or `d' into its appropriate member, and then pass
>the union as the actual argument.

I have a problem with using unions. (actually two problems.)

One is since I do not know the type until run time, I need
to create a union that contains EVERY possible type.  Now
what happens if I want to pass an array that is inside of an
array of structures? (for example)

Second, it would not be unreasonable to have someone
call this routine with a 10,000 element array of 128 bit
double floating point values.  I do not want to have to copy
this data at any time, and total storage (in memory) needs
to be kept as low as possible. [Of course these subroutines
need to be as fast as possible]

Stephen
-- 
Stephen J. Roznowski                 sjr@mimsy.umd.edu

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/15/88)

In article <14548@mimsy.UUCP> sjr@mimsy.umd.edu.UUCP (Stephen J. Roznowski) writes:
>The problem is that I can't determine the array type
>at compile time.  These subroutines are going to be part of
>a library.

The technique I suggested will work, although you might be better off
with separate functions to operate on different data types.

>And you are right that the pointers may be different,
>or at least have different alignment properties. Is this
>true of the pointer-to-void or pointer-to-char?

Any pointer type can be cast to either pointer-to-char or (if you have
it) pointer-to-void as a "canonical" pointer type to be passed through
a function parameter knothole having definite type, and inside the
function the conversion can be done in the reverse direction.  You
need to have additional information as to which type to convert back
to; this bit of information can be passed via another parameter.

If you can figure out how to avoid having a function with such a
messy interface, you'd be better off.

henry@utzoo.uucp (Henry Spencer) (11/16/88)

In article <14548@mimsy.UUCP> sjr@mimsy.umd.edu.UUCP (Stephen J. Roznowski) writes:
>The problem is that I can't determine the array type
>at compile time.  These subroutines are going to be part of
>a library.

In C, and on virtually all current machines, you *have* to determine the
array type at compile time, somehow, because the machine uses different
instructions to manipulate the different types.  The best you can do, on
the hardware level, is to have the code choose between several different
(pre-chosen, compiled-in) alternatives based on some indication of
what the type is for a particular invocation.  You'll have to pass in
some sort of type code, plus a pointer to the object (cast to "void *"
to standardize its representation), and then the code will have to look
at the type code, re-cast the pointer to the type it wants, and proceed.
-- 
Sendmail is a bug,             |     Henry Spencer at U of Toronto Zoology
not a feature.                 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

les@chinet.chi.il.us (Leslie Mikesell) (11/18/88)

>>	float	a[100];
>>	subroutine(...., a[100], ...);
>>	double	a[100];
>>	subroutine(...., a[100], ...);
>>and have the subroutine do the right thing with a[].

How about passing a struct containing a type code and pointers to
both types of arrays?  The "unused" item could be left null and
the type element would tell the function which pointer to use.

Les Mikesell

scs@athena.mit.edu (Steve Summit) (11/19/88)

In article <14457@mimsy.UUCP> sjr@mimsy.UUCP (Stephen J. Roznowski) writes:
>How do you pass the knowledge of the array type to the
>subroutine?

Interestingly enough, I have recently been contemplating adding
exactly this sort of feature to the C interpreter I fiddle with
from time to time.  The interpreter already has a "typeof"
command; for instance you can say

	ci> int (*pfi)();
	ci> typeof pfi
	pointer to function returning int

("typeof" is therefore half of cdecl).  As currently implemented,
"typeof" is a special command, not an operator like sizeof().
I realized that I could make it a true operator by

     1.	Adding a new type "type" to the internal list of types
	(int, long, double, etc.).

     2.	Having the typeof() operator return this new type-holding
	type.

     3.	Have the value printout routine (which prints the value
	of each "immediate mode" expression entered, in an
	appropriate format) print the cdecl form ("pointer
	to...") for an expression that returned type "type."

The next step is that you should also be able to use the "return
value" of this typeof operator in declarations and casts,
although it starts doing radical things to the grammar.  For
instance, you'd like to be able to say

	#define Swap(a, b)						\
		{							\
		typeof(a) tmp;	/* declare tmp having same type as a */	\
		tmp = a;						\
		a = b;							\
		b = tmp;						\
		}

but this means that the grammar for a declaration is (or
includes)

	declaration:
		expression declarator-list ;

with a semantic rule restricting the expression to be of type
"type."  This new production seems like it might introduce
ambiguities or unintended side effects.  Does it also mean that
the keyword "int" would now be an expression of type "type," or
should the above rule be in addition to the traditional

	declaration:
		declaration-specifiers declarator-list ;

Finally, what about casts?  You'd want to be able to say

	a = (typeof(b))c;

to cast c to b's type.  This form is even less likely to be
deterministically parseable.

This "meta-type" mechanism could conceivably handle Stephen's
problem well:

	main()
	{
	float	a[100];
	subroutine(typeof(*a), a[100]);
	}

	subroutine(t, a)
	type t;
	t a[];

Note that this example uses an explicit '*' on the array a as the
argument to typeof(), assuming that typeof(), like sizeof(), does
not implicitly convert arrays to pointers.  This example also
assumes that the declaration of the subroutine's formal parameter
"a" is "finished" at run time.  (Previous articles have discussed
supplying the dimensions of formal and/or local arrays via a
parameter at run time, which Fortran and gcc are able to do, but
is not standard C.)  Doing stuff on the fly like this is of
course trivial for an interpreter; in fact it's hard for the
interpreter not to allow things like this.  (My favorite is

	int array[getuid()];

which works just fine.)

In the above example, the fact that you'd also like to be able to
say

	subroutine(double, a2);

would be an argument in favor of redefining the primary type
names to be expressions of type "type."  (What kind of infinite
recursion does that apply for the new keyword "type" which
declares variables of type "type?")

Additional observations:

	These new "types" are really nothing more than run-time
	typedefs.

	They're really only implementable in an interpreter,
	since they defer almost everything to run time.

	Do pointers to these things ("type *tp;") mean anything?

I don't claim to have originated the idea of a type as a type;
I'm sure I've seen it in other languages.  (It's probably been
discussed here before as well.)

Please note that I am most emphatically not proposing this as an
extension to C, but merely speculating.  Since this is
far-fetched and not completely thought out, any comments should
perhaps be sent by mail rather than bewildering the net.

                                            Steve Summit
                                            scs@adam.pika.mit.edu