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