heins@orion.UUCP (Michael T. Heins) (10/13/86)
[]
Could someone suggest the proper way to handle the following problem?
I have a number of existing functions with compatible arguments. Some
are declared as void, and others as int. None of the return values are
used. I wish to set up an array of pointers to these functions.
My problem is that the compiler complains about incompatible types, and
I can't figure out how to use casts or unions to solve the problem.
Re-declaring the functions is not an option. I have exemplified the
situation below:
int fna() { }
void fnb() { }
int (*array[32])();
main() {
array[0] = fna;
array[1] = fnb; /* This won't work as-is. */
}
I have tried things like
array[1] = (int (*)())fnb;
but this generates the message "operands of CAST have incompatible types".
Any help would be appreciated.
--
...!hplabs!sdcrdcf!trwrb!orion!heins
We are a way for the universe to know itself. -- Carl Sagan
tps@sdchem.UUCP (Tom Stockfisch) (10/15/86)
In article <26@orion.UUCP> heins@orion.UUCP (Michael T. Heins) writes: > >int fna() { } > >void fnb() { } > >int (*array[32])(); >main() { > array[0] = fna; > array[1] = fnb; /* This won't work as-is. */ >} > >I have tried things like > array[1] = (int (*)())fnb; >but this generates the message "operands of CAST have incompatible types". The compiler rightly complains because void (*)() could conceivably be larger than int (*)() on some wierd machine. You should use a union, e.g.: union int_or_void_func { int (*int_func)(); void (*void_func)(); } array[32]; main() { array[0].int_func = fna; array[1].void_func = fnb; } The above is the much preferred way to do this. However, if you want to initialize "array[]", you currently can't use a union (ANSI standard may change this). Then the portable way to do this is int fna(); void fnb(); char *array[32] = { (char *)fna, (char *)fnb }; main() { ( *(int (*)())array[0] )(); /* call fna */ ( *(int (*)())array[1] )(); /* call fnb */ } If this won't work on some machines, I would appreciate hearing an explanation from some guru. A disadvantage of this method, besides ugliness, is that lint gives the complaint questionable conversion of function pointer for each element of array that you initialize, and each call as well. In any application where I use this method I wind up creating a lint filter such as grep -v 'questionable conversion of function pointer' and use it on all lint output. -- -- Tom Stockfisch, UCSD Chemistry
bright@dataio.UUCP (10/15/86)
In article <26@orion.UUCP> heins@orion.UUCP (Michael T. Heins) writes: >I have a number of existing functions with compatible arguments. Some >are declared as void, and others as int. None of the return values are >used. I wish to set up an array of pointers to these functions. >My problem is that the compiler complains about incompatible types, and >I can't figure out how to use casts or unions to solve the problem. >Re-declaring the functions is not an option. I have exemplified the >situation below: > >int fna() { } >void fnb() { } > >int (*array[32])(); >main() { > array[0] = fna; > array[1] = fnb; /* This won't work as-is. */ >} > >I have tried things like > array[1] = (int (*)())fnb; >but this generates the message "operands of CAST have incompatible types". int fna() { } void fnb() { } int callfnb() { fnb(); } int (*array[32])(); main() { array[0] = fna; array[1] = callfnb; } Messy, but workable.
jnm@Sun.COM (10/16/86)
Interestingly, you second example, using array[1] = (int(*)())fnb; works fine on my machine (A Sun Workstation)... Here is some code I wrote to achieve what you were trying to do using alternative methods which may be useful. I included the typedef's to make things more legible... 1. The following program compiles ... int fna() { } void fnb() { } typedef int (*pfi)(); typedef void (*pfv)(); pfi array[32]; main() { array[0] = fna; array[1] = (pfi)fnb; } 2. Use Unions instead of casting ... int fna() { } void fnb() { } typedef int (*pfi)(); typedef void (*pfv)(); struct { union { pfi fnint; pfv fnvoid; } Union; } array[32]; main() { array[0].Union.fnint = fna; array[1].Union.fnvoid = fnb; } Hope that's useful Jonathan sun!sunuk!jmills
rgenter@labs-b.bbn.com (Rick Genter) (10/16/86)
This is an artifact of "void is a second-class citizen" on the 4.2 BSD compiler. Your example works under the Sun Unix 3.0 C compiler. - Rick -------- Rick Genter BBN Laboratories Inc. (617) 497-3848 10 Moulton St. 6/512 rgenter@labs-b.bbn.COM (Internet new) Cambridge, MA 02238 rgenter@bbn-labs-b.ARPA (Internet old) seismo!bbncca!rgenter (UUCP)
throopw@dg_rtp.UUCP (10/18/86)
> heins@orion.UUCP (Michael T. Heins) > My problem is that the compiler complains about incompatible types, and > I can't figure out how to use casts or unions to solve the problem. Well, the cast solution you gave ought to have worked. And, indeed, it works for the compiler and lint that I use. The draft ANSI C document has this to say, in 3.3.4: A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function of other than the original type, the behavior is undefined. So, the good news is that draft ANSI says the cast must work. On the other hand, the bad news is that you can't portably use the cast pointer to invoke the function. Sigh. On the third hand, almost all extant C compilers (which support the relevant types) will work correctly when a pointer expression of type (int (*)()) is used to invoke a function of type (void ()). On the fourth hand (look, ma! four hands!), if you *really* want to be portable, you might should do something like this: /* to declare the array of functions to call */ typedef union { void (*v)(); int (*i)();} func_ptr_t; typedef enum {void_func, int_func} func_kind_t; typedef struct { func_kind_t kind; func_ptr_t ptr; } func_desc_t; func_desc_t func_desc[N]; /* to install a new function */ switch(func_kind){ case(void_func): func_desc[i].ptr.v = some_void_func; break; case(int_func): func_desc[i].ptr.i = some_int_func; break; } func_desc[i].kind = func_kind; /* to invoke a function */ switch(func_desc[i].kind){ case(void_func): (*func_desc[i].ptr.v)(); break; case(int_func): (*func_desc[i].ptr.i)(); break; } OK, OK, so it's ugly *ugly* *UGLY*!!! On the other hand, it is the simplest portable method I've come up with that does the job and directly invokes the function. It is also possible to do it a more flexible way which trades an extra function invocation for the switch at invocation time (and which can be expanded to invoke more types of functions without changing the invocation code). This method isn't really any simpler in its general form, and is left as an excersize for the masochistic reader. -- Like punning, programming is a play on words. --- Alan J. Perlis -- (By the way, "might should" (along with "might could" and other might-modified forms) is a feature of the local dialect that I threw in there on purpose for whimsical effect. I did realize it isn't quite correct when I did it. Really. No, wait, I really *did* do it on purpose! Where's everybody going? Don't you believe me? You can't just walk out on me like this!! COME BACK! WAIT... oh, what's the use? At least I linted my examples... hmpf!) -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
chris@umcp-cs.UUCP (Chris Torek) (10/22/86)
It is amazing how much discussion one compiler bug can generate! >In article <26@orion.UUCP> heins@orion.UUCP (Michael T. Heins) writes: >> >>int fna() { } >> >>void fnb() { } >> >>int (*array[32])(); >>main() { >> array[0] = fna; >> array[1] = fnb; /* This won't work as-is. */ >>} >> >>I have tried things like >> array[1] = (int (*)())fnb; >>but this generates the message "operands of CAST have incompatible types". The cast is correct, but the 4.1 and 4.2BSD compilers misunderstand void functions. `void' types in these compilers tend to cause all sorts of internal indigestion. This is a bug. In article <393@sdchema.sdchem.UUCP> tps@sdchema.UUCP (Tom Stockfisch) writes: >The compiler rightly complains because > void (*)() >could conceivably be larger than > int (*)() >on some wierd machine. This is true, but that does not mean the compiler should complain, or at least not so vociferously. At most it should issue a warning. Lint should note the cast as non-portable. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu
throopw@dg_rtp.UUCP (Wayne Throop) (11/04/86)
> chris@umcp-cs.UUCP (Chris Torek) > The cast is correct, but the 4.1 and 4.2BSD compilers misunderstand > void functions. -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
throopw@dg_rtp.UUCP (Wayne Throop) (11/04/86)
> chris@umcp-cs.UUCP (Chris Torek) > The cast is correct, but the 4.1 and 4.2BSD compilers misunderstand > void functions. `void' types in these compilers tend to cause all > sorts of internal indigestion. This is a bug. Exactly right, of course. > Lint should note the cast as non-portable. 99 and 44/100 percent right. The *cast* is portable. But invocation of a function via a cast pointer to a function of differing type is not. Of course, lint may not be able to catch such subtleties easily, and the cast itself might be an appropriate place to warn. -- "Greater nits have little nits, upon their backs to bite 'em. And little nits have lesser nits, and so ad-infinitum..." --- (can't recall who I'm paraphrasing... Ogden Nash probably...) -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw