jlh@loral.UUCP (Jim Vaxkiller) (01/10/87)
I'm having a little problem with tables of pointers to functions. What I want to do is have several functions returning type void, then put their addresses into a table and execute the function by indexing into the table. Assembly language programmers out there will recognize this as a simple jump table. Now if I have my functions return ints I'm fat, dumb, and happy. It's when I try to use functions returning voids that the compiler burps. I know about the void problem in the 4.2BSD compiler and have typedef'd voids to ints, but to no avail. The following code fragment illustrates my problem. void /* 2 do nothing routines returning */ err0() /* different data types */ {} int err1() {} static (*ptr_tbl[])() = { /* table of 2 function pointers */ err0, /* this is what the compiler hates */ err1 }; main() { (*ptr_tbl[0])(); /* invoke function */ } Granted, I could have my functions all return ints, but I don't want to do that because they don't return anything and lint complains. My system is a vax 11/750 running 4.2BSD UN*X. Any suggestions on how to make this work would be much appreciated. Jim Jim Harkins Loral Instrumentation, San Diego {ucbvax, ittvax!dcdwest, akgua, decvax, ihnp4}!sdcsvax!sdcc6!loral!jlh
amamaral@elrond.UUCP (Alan Amaral) (01/12/87)
In article <1327@loral.UUCP>, jlh@loral.UUCP (Jim Vaxkiller) writes: > I'm having a little problem with tables of pointers to functions. > What I want to do is have several functions returning type void, > then put their addresses into a table and execute the function by > indexing into the table. Assembly language programmers out there > will recognize this as a simple jump table. Now if I have my functions > return ints I'm fat, dumb, and happy. It's when I try to use functions > returning voids that the compiler burps. I know about the void problem > in the 4.2BSD compiler and have typedef'd voids to ints, but to no avail. > The following code fragment illustrates my problem. > > > void /* 2 do nothing routines returning */ > err0() /* different data types */ > {} > > int > err1() > {} > > static (*ptr_tbl[])() = { /* table of 2 function pointers */ > err0, /* this is what the compiler hates */ > err1 > }; > > main() > { > (*ptr_tbl[0])(); /* invoke function */ > } > > > Granted, I could have my functions all return ints, but I don't want > to do that because they don't return anything and lint complains. > My system is a vax 11/750 running 4.2BSD UN*X. Any suggestions on > how to make this work would be much appreciated. > > > Jim > > Jim Harkins > Loral Instrumentation, San Diego > {ucbvax, ittvax!dcdwest, akgua, decvax, ihnp4}!sdcsvax!sdcc6!loral!jlh Here is one way: void /* 2 do nothing routines returning */ err0() /* different data types */ {printf("testing 123\n");} int err1() {} void (*ptr_tbl[2])(); /* table of 2 function pointers */ void init() { ptr_tbl[0] = err0; } main() { init(); (*ptr_tbl[0])(); /* invoke function */ } Here is another way: int /* 2 do nothing routines returning */ err0() /* different data types */ {} int err1() {} static (*ptr_tbl[])() = { /* table of 2 function pointers */ err0, /* this is what the compiler hates */ err1 }; main() { (void) (*ptr_tbl[0])(); /* invoke function */ } Neither causes complaints from cc or lint (except for the unused err1 in the first example that is). I have found that this method is VERY nice for doing lots of different things cleanly. For example, I have a Ray Tracing program that uses this sort of mechanism all over the place for keeping methods straight for each object that I am rendering. For example: /* intersect ray with all known objects */ for(i=0; i<n_objects; i++) (* object_list[i].intersect) (i,ray); causes the intersection algorithm for each object to be called, causing each object to intersect itself with the supplied ray. This wouldn't be necessary normally except that each object type has a different mechanism and routine for the intersection process. It could also be done by calling an intersection routine that had a case statement and checked what type of object each object was and called the right algorithm, but this is so much cleaner. Also, to add a new object is almost trivial after the object specific code is written. The mechanism is as follows: In the initialization routine an array entry is filled with the address of a routine that reads the data for each specific object type. When an object type is encountered the jump table causes the corresponding routine to be called. In the object read routine the data is read and the address's for all the other methods (intersection, shadowing, and attributes i.e. color or texture) are filled into the object structure. When the object is to be intersected, shadowed, or the attributes of the object at a particular point are requested, the requesting routine indexes into the object_list array (as above) and calles the correct routine with the correct parameters. This has a very clear advantage over every other method I have ever used in that to add a new object type, all I have to do is to add a new line in the initialization routine to load the object read routine, and the object read routine fills in the rest. It's especially nice in that I can supply only the code that needs to be changed (the init routine) in source format, and the rest in object format, and the user can add new objects just like a UNIX s/p can add a new device driver. -- uucp: ...decvax!elrond!amamaral I would rather be a phone: (603) 885-8075 fool than a king... us mail: Calcomp/Sanders DPD (PTP2-2D01) Hudson NH 03051-0908
throopw@dg_rtp.UUCP (Wayne Throop) (01/12/87)
> jlh@loral.UUCP (Jim Vaxkiller) > I'm having a little problem with tables of pointers to functions. > What I want to do is have several functions returning type void, > The following code fragment illustrates my problem. > void err0() {} > int err1() {} > static (*ptr_tbl[])() = { err0, err1 }; > main() { (*ptr_tbl[0])(); } You have declared ptr_tbl to be an array of pointers to functions returning int, and err0 is a pointer to a void function. You must use casts or unions if you wish to have an array of pointers to functions of more than one type. Unions are probably the best method, though there are problems with initializers for unions. I don't see any portable way to statically initialize an array of pointers to functions of differing types. -- You see, wire telegraph is a kind of very, very long cat. You pull his tail in New York and his head is meowing in Los Angeles. Do you understand this? And radio operates exactly the same way: you send signals here, they receive them there. The only difference is that there is no cat. --- Albert Einstein -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
ron@brl-sem.ARPA (Ron Natalie <ron>) (01/15/87)
In article <1327@loral.UUCP>, jlh@loral.UUCP (Jim Vaxkiller) writes: > I'm having a little problem with tables of pointers to functions. > What I want to do is have several functions returning type void, > then put their addresses into a table and execute the function by > indexing into the table. > The following code fragment illustrates my problem. > > void /* 2 do nothing routines returning */ > err0() /* different data types */ > {} > int > err1() > {} > static (*ptr_tbl[])() = { /* table of 2 function pointers */ > err0, /* this is what the compiler hates */ > err1 > }; Hold the phone! Lets look at what you've done. First..ptr_tbl is declared as an array of pointers to functions returning INT. If you don't specify a type it is INT. You could fix problem #1 by making it read "static void (*ptr_tbl[])()" Second. You do not have a set of functions returning VOID or INT. You have a combination. You seem to expect clairvoyance by the compiler. It is required that when the compiler calls a function that it knows what type the function is going to return. This is required because calls to functions returning different types may require different linkages even if you are not going to look at the return value. For example, functions returning struct usually want to write the return value into some allocated area on the stack which you must set up even if you are casting the return to void. If you generate a table of mixed types then how is the compiler going to know which linkage to use for which function. C has to "typeof" operator that can be applied to contents of a pointer to find out what the pointer was pointing to (although some architectures actually support this). Make all your functions return the same type. In this case, void. If you have a function returning something else, then encapsulate it in a void function, e.g... struct foo goo(); void vgoo() { (void) goo(); } and include vgoo in your table. -Ron
ron@brl-sem.ARPA (Ron Natalie <ron>) (01/15/87)
In article <592@elrond.UUCP>, amamaral@elrond.UUCP (Alan Amaral) writes: > In article <1327@loral.UUCP>, jlh@loral.UUCP (Jim Vaxkiller) writes: > Here is one way: > > void /* 2 do nothing routines returning */ > err0() /* different data types */ > {printf("testing 123\n");} > > int > err1() > {} > > void (*ptr_tbl[2])(); /* table of 2 function pointers */ > > void init() > { > ptr_tbl[0] = err0; > } WARNING WARNING...DANGER DANGER...RUN, WILL ROBINSON, RUN! Gak...don't do this. While most compilers will treat this OK since the place where an "int" is returned is generally considered scratch before a funcion is called, this is NOT PORTABLE, and will break most compilers if err0 returned something large like a structure. The linkage of a subroutine is dependant on the value it returns. Casting it into a pointer to a function returning a different type is asking for trouble. -Ron
latham@bsdpkh.UUCP (01/16/87)
in article <1327@loral.UUCP>, jlh@loral.UUCP (Jim Vaxkiller) says: > Keywords: (*ptr[i])() = ??? > > I'm having a little problem with tables of pointers to functions. > What I want to do is have several functions returning type void, Try using the following ..... void err1() {} /* NOTE : these are functions returning void ! void err2() {} extern void err3(); static void (*ptr_tbl[])() = { err1, /* this is a table of pointers to functions */ err2, /* that return void ....... */ err3 /* previously you defined them as returning int */ /* because you only stated that they we static */ /* thus they returned int BY DEFAULT */ }; main() { (*ptr_tbl[0])(); /* invoke function */ } A POINTER TO A FUNCTION HAS A TYPE ... it is the type that the function returns. If you wish to set up a table of said pointers, then you must declare its elements as pointers to functions returning that type. I HAVE TRIED THE ABOVE .... to avoid foot in mouth disease. The only reason it didn't compile was that err3 was an undefined symbol. (sysVr2.1) ************************************************************************ "There is not now, nor will there ever be, a computer language in which it is the least bit difficult to write bad programs." -Unknown Ken Latham uucp: ..!ihnp4!bsdpkh!latham P.S. The quote in the signature is not directed at the writer of the original article .... it is just somthing I believe.
bobc@attctc.Dallas.TX.US (Bob Calbridge) (07/18/89)
Well, here I am again. Stuck in that twilight zone where I know just enough to get me into trouble. What seems obvious isn't. I should mention that I am using Turbo C for this. Here's the problem. I'm attempting to use the windowing functions of a library called CXL to design a utility program. The function I'm having problems with are is called "wmenuitem". Its declaration is: int _Cdecl wmenuitem(int wrow,int wcol,char *str,int schar,int tagid, int fmask,void (_Cdecl *select)(void), unsigned hotkey,int help); Following is an extract of the components of the function: ------------------------------------------------------------------------ NAME wmenuitem DESCRIPTION Defines a menu item. This is one of the 4 functions that are required to process a menu. SYNOPSIS #include "cxlwin.h" int wmenuitem(int wrow,int wcol,char *str,int schar,int tagid, int fmask,void (*select)(void),unsigned hotkey, int help); INPUTS wrow - window row wcol - window column str - address of menu item string schar - quick selection character tagid - unique tag identifier of this particular menu item. This is the value that wmenuget() returns upon its selection. fmask - feature mask. Allows you to define one or more additional features for this menu item. Valid features are: M_HASPD - has a pull-down menu attached M_NOSEL - menu item is not selectable M_CLOSE - close this menu when item is selected M_CLALL - close all menus when item is selected More than one feature can be specified by using the C bitwise OR operator '|'. For example, if this item has a pull-down menu attached and it is not selectable, you would specify (M_HASPD|M_NOSEL). Specify 0 if you don't want to define an fmask for this item. select - address of the function to call upon selection of this menu item. Specify NULL if you don't want to define a select function. hotkey - keycode of the key, which when pressed, will select this menu item's function from anywhere within the menu structure. This allows the user to call this menu item's select function even if not currently processing its menu. See Appendix B for a list of keycodes that you can use. Specify 0 if you don't want to define a hotkey. help - help category number to be associated with this item. Specify 0 is you don't want to define a help category for this item. RETURN VALUE W_NOERROR - no error W_NOMNUBEG - no begin of menu specified. See wmenubeg(). W_ALLOCERR - memory allocation error EXAMPLE wmenuitem(0,0,"Load F3",'L',1,M_CLOSE,load_file,0x3d00,0); ------------------------------------------------------------------------ Now, here's a piece from my program: wmenubeg (27,9,53,20,5,0,_BLUE|WHITE,NULL); wmenuitem (1,2,"Build a configuration",'B',1,0,build_config(),0,0); wmenuitem (2,2,"Change a configuration",'C',2,0,change_config(),0,0); wmenuitem (3,2,"Convert a file",'F',3,0,conv_file(),0,0); wmenuend (1, M_VERT|M_PD|M_SAVE, 0, 0, _BLUE+WHITE, RED, LGREY, YELLOW); wmenuget (); There are several problems that arise from my code depending on what tact I take. In the form above the compiler reports "Non-portable pointer conversion". This is more of a warning than an error. The cursor positions itself after the final '0' in the parameters. If, per the example, I leave the '()'s off the function in the select parameter it says "Undefined symbol 'build_config'". In this case the cursor is positioned following the function name "build_config". In other instances, which I have been unable to reproduce after having fiddled with the code, produce error messages that specify that the error was produced in "select" and another message that demands a memory address. The structure of the code above is intended to produce a bar menu which acts upon the menuitems when selected. In the code segment above, with the non-portable pointer conversion, the first menu item is initiated without producing a window. Can anyone tell me (1) in what way my code differs from the example, that I need to put the "()"s after the function name and (2) what the problem is with the non-portable pointer conversion, does it cause the code to run improperly and should I worry about it. Sorry for the band width but I'm still learning. Thanks. Bob the -- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- = I know it's petty.......... = - But I have to justify my salary! - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Bob.Stout@p6.f506.n106.z1.fidonet.org (Bob Stout) (07/20/89)
In an article of <18 Jul 89 01:19:58 GMT>, (Bob Calbridge) writes: >Here's the problem. I'm attempting to use the windowing functions of a >library called CXL to design a utility program. Excellent choice... >Now, here's a piece from my program: > >wmenubeg (27,9,53,20,5,0,_BLUE|WHITE,NULL); > wmenuitem (1,2,"Build a configuration",'B',1,0,build_config(),0,0); > ... Assuming that build_config() and your other functions are properly declared and/or prototyped, this is almost right. Leave off the "()" when including the function in the call to wmenuitem(). The way you have this written, what's being passed to wmenuitem() is the return value from calling build_config() with no arguments. Simply sayding (...,build_config,...) will put the address of the build_config() function into the parameter list for wmsnuitem() - but only, as I mentioned, if it's been previously declared.