[net.lang.c] Casting void - int question

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