[comp.lang.c] Varargs problem

bobg+@andrew.cmu.edu (Robert Steven Glickstein) (08/26/89)

I wish to extract a variable of type "pointer to function returning int"
[(int (*) ())] from a varargs parameter list, and I can't.  My function
looks like the usual:

    foo(va_alist) va_dcl
    {
        va_list ap;
        int (*fn)();

        va_start(ap);
        fn = va_arg(ap, int (*) ());
        va_end(ap);
    }

The problem, at least in my OS's implementation, is that the va_arg
macro is defined as

    # define va_arg(list,mode) \
        ((mode *)((list += (sizeof(mode)+3)&(-4))\
        -((sizeof(mode)<4)?sizeof(mode):\
        (sizeof(mode)+3)&(-4))))[0]

and a typecast operator like (int (*) ()) is turned into (int (*) () *)
by the macro, on which the compiler barfs.

I might be able to do

    fn = (int (*) ()) va_arg(ap, char *);

but I don't know how portable this is, and I'd like to do this in a
highly portable fashion.  Any suggestions?

_______________________________
Bob Glickstein, System Designer
Information Technology Center  room 220
Carnegie Mellon University
Pittsburgh, PA  15213-3890
(412) 268-6743

Internet: bobg+@andrew.cmu.edu
UUCP: ...!harvard!andrew.cmu.edu!bobg

A total abstainer is one who abstains from everything but abstention,
and especially from inactivity in the affairs of others.
		-- Ambrose Bierce, "The Devil's Dictionary"

aw0g+@andrew.cmu.edu (Aaron Wohl) (08/26/89)

Bobg,

As a rabid varargs user, I can attest to the wonders of never giving
anything fancy to varags.  Always use a typedef.  How about:

#include <varargs.h>

typedef (*int_func)();

void mumble(va_alist)
va_dcl
{
        va_list ap;
        int_func fn;
        va_start(ap);
        fn=va_arg(ap,int_func);
        frotz(fn);
}

Aaron

chris@mimsy.UUCP (Chris Torek) (08/26/89)

In article <4YxPuc200VsnE_B3Jy@andrew.cmu.edu> bobg+@andrew.cmu.edu
(Robert Steven Glickstein) writes:
>I wish to extract a variable of type "pointer to function returning int"
>[(int (*) ())] from a varargs parameter list, and I can't.
>        fn = va_arg(ap, int (*) ());

va_arg must be handed a type that can legally have one `*' added on the
end; int (*)() is not such a type.

The solution is to create a typedef:

	typedef int (*intfn_t)();
	...
	fn = va_arg(ap, intfn_t);

With one small change (`typedef int (*intfn_t)(void)') one gets an ANSI-
style extraction.  (Alas, the `va_start' invocations and function
declarations must be changed.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/28/89)

In article <4YxPuc200VsnE_B3Jy@andrew.cmu.edu> bobg+@andrew.cmu.edu (Robert Steven Glickstein) writes:
>Any suggestions?

Use a typedef.  va_arg(ap, mytype) should always work.

MARWK@levels.sait.edu.au (08/28/89)

In article <4YxPuc200VsnE_B3Jy@andrew.cmu.edu>, bobg+@andrew.cmu.edu (Robert Steven Glickstein) writes:
> I wish to extract a variable of type "pointer to function returning int"
> [(int (*) ())] from a varargs parameter list, and I can't. 
> 
> I might be able to do
> 
>     fn = (int (*) ()) va_arg(ap, char *);
> 
> but I don't know how portable this is, ...

I am not an expert, but it seems to me that one can always cast a pointer
to a pointer of another type, so the above is portable - a problem might
occur if one has a FAR pointer cast to a NEAR pointer, but this is architecture
specific anyway.

Ray

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/29/89)

In article <1301@levels.sait.edu.au> MARWK@levels.sait.edu.au writes:
>I am not an expert, but it seems to me that one can always cast a pointer
>to a pointer of another type, ...

No, in fact conversion between object and function pointers is officially
disallowed.  (It surprised me when I first heard about it, but after
pondering the point for a while I decided that was proper.)  Conversion
among object (data) pointers is constrained by the natural alignment
requirements only.

On a true segmented architecture, for example, it might take more bits
to represent a function address than for any data address within a task,
because function pointers would have to include segment identification
while with proper C compiler implementation all the process's data
would be in a single known segment.

cowan@marob.masa.com (John Cowan) (08/30/89)

In article <10863@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>On a true segmented architecture, for example, it might take more bits
>to represent a function address than for any data address within a task,
>because function pointers would have to include segment identification
>while with proper C compiler implementation all the process's data
>would be in a single known segment.


I realize I may be starting a flame war here, but what is supposed to count
as a "true" segmented architecture?  I immediately concede that the 8086 doesn't
have such a thing, but I contend that the 80286 running in protected mode does.
It has segment tables, segment length control, etc. etc.

Nevertheless, "far" object pointers are still necessary on such an architecture
if a process is allowed to access more than 64K of data.  Even on the '386,
where segments are allowed to be up to 4G, The Day Will Come, My Friends, when
processes routinely need more than that!  Back to "far" pointers.

Perhaps there is some clever compiler technology that allows one to have more
bytes in a segment than the processor architecture allows, but if so, I can't
imagine what it might be.
-- 
Internet/Smail: cowan@marob.masa.com	Dumb: uunet!hombre!marob!cowan
Fidonet:  JOHN COWAN of 1:107/711	Magpie: JOHN COWAN, (212) 420-0527
		Charles li reis, nostre emperesdre magnes
		Set anz toz pleins at estet in Espagne.

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/01/89)

In article <24FC0BD3.4529@marob.masa.com> cowan@marob.masa.com (John Cowan) writes:
>I realize I may be starting a flame war here, but what is supposed to count
>as a "true" segmented architecture?  I immediately concede that the 8086 doesn't
>have such a thing, but I contend that the 80286 running in protected mode does.
>It has segment tables, segment length control, etc. etc.

What I had in mind was a Burroughs 6700-like architecture with all data
accesses implied as offsets from the relevant segment base.  I can
imagine needing more bits for code switch to another segment than for
data access switch between segments.  I don't know if any machines have
been built that actually are like that, but since code and data are
treated entirely separately on some existing machines, it is not entirely
out of the question.