[comp.lang.c] Casting function ptrs

daveh@marob.MASA.COM (Dave Hammond) (05/19/88)

Given a list of varying typed functions, their names and addresses:

struct funcs {
    char *name;
    unsigned (*func)();
}
char *foo();
struct funcs f = {  "foo", (char *)foo };

And a routine which is passed a function name in order to lookup and return
its associated address: 

unsigned *lookup_func(name)
char *name;
{
  ...
  return( (unsigned *) f->func );
}

How does one declare the lookup function, the return address variable and
call the returned function, while properly casting the function return value?
I'm doing it like so:

unsigned *lookup_func();
unsigned *(*func)();
char *retp;

if ((func = lookup_func("foo")) != (unsigned *)0) {
    if (( retp = (char *) (*func)() ))
	printf("retp=%s",retp);
    }

Yes? No? Gonna blow up on me at some point ?


Dave Hammond
DSI Communications, Inc.
--------------------------------
UUCP:   ...!marob!daveh
VOICE:  (516)872-3535
USMAIL: 333 W. Merrick Rd.
        Valley Stream, NY 11580
--------------------------------

chris@mimsy.UUCP (Chris Torek) (05/19/88)

In article <281@marob.MASA.COM> daveh@marob.MASA.COM (Dave Hammond) writes:
>Given a list of varying typed functions,

Danger Will Robinson :-)

`Varying typed' should trigger alarm bells.

>their names and addresses ... and a routine which is passed a function
>name in order to lookup and return its associated address ...
>How does one declare the lookup function, the return address variable and
>call the returned function, while properly casting the function return value?

Depending on the type variation, there may not be a `proper cast'.
Some types may be mixed (e.g., pointers, via `void *' per the dpANS,
or integral types, via the longest of the bunch).  If there is no
common type, you must use a union:

	/* function-pointer union */
	union fpu { /* abbreviation `f.u.' avoided for obvious reasons :-) */
		long	(*ifn)(void);	/* for integral functions */
		double	(*fn)(void);	/* for floating functions */
		void	*(*ptrfn)(void);/* for object-pointer functions */
		void	(*(*codefn)())();/* for code-pointer functions */
		void	*(*(*pcfn)())();/* for ptr valued code-ptr fns */
		/* &c ad nauseam */
	};
	struct ftable {
		char	*name;		/* the function's name */
		int	type;		/* its return type */
		union	fpu fn;		/* and its address */
	};

Alas, you cannot initialise a union except via its first member,
and that only in dpANS-conformant compilers.

Assuming that all the functions return pointers, you can resort to
this (dropping the prototypes):

	typedef void *PTR;	/* or char *, if void * fails */
	/* optional: */
	typedef PTR (*PFP)(); /* ptr to function returning generic ptr */

	unsigned *a();
	char *b();
	double *c();

	enum ftype { F_UNSIGNED, F_CHAR, F_DOUBLE };
	struct ftable {
		char	*name;
		enum	ftype ftype;
		PTR	(*fn)();	/* or PFP fn */
	} ftable[] = {
		"a",	F_UNSIGNED,	(PTR (*)())a,	/* or (PFP)a */
		"b",	F_CHAR,		(PTR (*)())b,	/* etc */
		"c",	F_DOUBLE,	(PTR (*)())c
		0
	};

>I'm doing it like so:
>
>unsigned *lookup_func();
>unsigned *(*func)();
>char *retp;
>
>if ((func = lookup_func("foo")) != (unsigned *)0) {
>    if (( retp = (char *) (*func)() ))
>	printf("retp=%s",retp);
>    }

Try
	/* clearest: */
	PFP	lookup_func();
	/* expanded one level: */
	/* PTR (*lookup_func())(); */
	/* whole hog: */
	/* void *(*lookup_func())(); */

	PFP	func;
	char	*retp;

	/* also make sure we get the right type back: */
	if ((func = lookup_func("foo", F_CHAR)) != NULL)
		if ((retp = (char *)(*func)()) != NULL)
			printf("retp=%s", retp);

One might wonder, given the less clear `lookup_func' declarations
above, where the arguments go:

	void *(*lookup_func(name, type))()
		char *name;
		enum ftype type;
	{
		register struct ftable *p;

		for (p = ftable; p->name != NULL; p++) {
			if (p->ftype != type)
				continue;
			if (strcmp(p->name, name) == 0)
				return (p->fn);
		}
		return (NULL);
	}

It is rather simpler to write

	PFP lookup_func(name, type)
		char *name;
		enum ftype type;
	{ ...

as I think everyone will agree :-) .
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

djones@megatest.UUCP (Dave Jones) (05/20/88)

in article <281@marob.MASA.COM>, daveh@marob.MASA.COM (Dave Hammond) says:
> 
> 
> Given a list of varying typed functions, their names and addresses:
> 
> struct funcs {
>     char *name;
>     unsigned (*func)();
> }
> char *foo();
> struct funcs f = {  "foo", (char *)foo };

...

typedef unsigned (*func_ptr)();

struct funcs {
  char* name;
  func_ptr func;
} func_table;

func_ptr
func_lookup()
{ ... whatever ... }

{
  func_ptr proc;
  char* func_name = ... whatever ...;
  unsigned result;

  if((proc = func_lookup(func_name)) != (func_ptr)0)
	result = (*proc)();
  else
	fprintf(stderr, "Shucks.\n");
}

walter@hpcllz2.HP.COM (Walter Murray) (05/21/88)

Dave Hammond writes:

>Given a list of varying typed functions, their names and addresses:
>And a routine which is passed a function name in order to lookup and return
>its associated address: 
>How does one declare the lookup function, the return address variable and
>call the returned function, while properly casting the function return value?
>I'm doing it like so:
[all examples deleted]
>Yes? No? Gonna blow up on me at some point ?

Dave, if I understand you right, when you call lookup_func("foo"),
you know what type foo() is going to return, and you just need to look
up its address.  If that's the case, I think the following is what
you want.  It's close to what you had, and lint likes it. Other things
will work, but I think the following has the virtue of being strictly
conforming per the dpANS.

#include <stdio.h>
#include <string.h>
struct funcs {char *name; void (*func)();};
char *foo() {return "It worked!";};
struct funcs f = {"foo",(void(*)())foo};
void (*lookup_func(name))()
   char *name;
{
   if (!strcmp (name, f.name)) return f.func;
   else return 0;
}
void (*func)();
char *retp;
main ()
{
   if ((func = lookup_func ("foo")) != 0) 
      if ((retp = ((char*(*)())func)()) != 0)
	 (void) printf ("retp = %s\n",retp);
   return 0;
}

Walter Murray

All opinions expressed are my own.

chris@trantor.umd.edu (Chris Torek) (05/23/88)

In article <7330003@hpcllz2.HP.COM> walter@hpcllz2.HP.COM (Walter Murray)
writes:
>... but I think the following has the virtue of being strictly
>conforming per the dpANS. ...
>
>struct funcs {char *name; void (*func)();};
>char *foo() {return "It worked!";};
>struct funcs f = {"foo",(void(*)())foo};

This is not strictly conforming, because the standard does not say
whether objects of type `void (*)()' are in any way compatible with
objects of type `char *(*)()'.  (It says very little about pointers
to functions, no matter what the return type of the function.  It
does not even say whether `void *' and `t (*)()' are compatible.)
Since, however, `char *' and `void *' are `the same', using a base
type of `void *(*)()' should work.

Most likely, pointers to any function, no matter what the return
type, will have the same format, but it would be nice to have some
guarantees.
-- 
In-Real-Life: Chris Torek, Univ of MD Computer Science, +1 301 454 7163
Domain: chris@mimsy.umd.edu		Path: ...!uunet!mimsy!chris

walter@hpcllz2.HP.COM (Walter Murray) (05/23/88)

chris@trantor.umd.edu (Chris Torek) writes:

>In article <7330003@hpcllz2.HP.COM> walter@hpcllz2.HP.COM (Walter Murray)
>writes:

>>... but I think the following has the virtue of being strictly
>>conforming per the dpANS. ...
>>
>>struct funcs {char *name; void (*func)();};
>>char *foo() {return "It worked!";};
>>struct funcs f = {"foo",(void(*)())foo};

>This is not strictly conforming, because the standard does not say
>whether objects of type `void (*)()' are in any way compatible with
>objects of type `char *(*)()'.  (It says very little about pointers
>to functions, no matter what the return type of the function....)
>
>Most likely, pointers to any function, no matter what the return
>type, will have the same format, but it would be nice to have some
>guarantees.

I was relying on the following statement from page 47 of the January
draft:  "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."

That sounds to me like a guarantee.  Did I read too much into it?

Walter Murray

All opinions expressed are my own.

chris@mimsy.UUCP (Chris Torek) (05/25/88)

>chris@trantor.umd.edu (me, using trantor while the broadband network
was out), write:
>>This is not strictly conforming, because the standard does not say
>>whether objects of type `void (*)()' are in any way compatible with
>>objects of type `char *(*)()'. ...

In article <7330004@hpcllz2.HP.COM> walter@hpcllz2.HP.COM (Walter Murray)
writes:
>... page 47 of the January draft:  "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."

Oops.  You are right; I missed this paragraph.

(Though I wonder whether some implementor might construe this to mean
that the result need only *compare equal* but not necessarily be
*useful*.  But that would be stupid.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris