[comp.lang.c] function pointer help needed

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.