[comp.lang.c] varargs...help appreciated

kenc@madmax.Viewlogic.COM (Kenstir) (11/03/90)

Can you argument lists two levels down?  I passed an argument list
to a function, took a couple of parameters off, then tried to pass
the rest of the argument list down to another function.  At this
point, the parameters are no good.  Yet, it seems to work with vprintf
and the like.  Some code follows (sorry, this is as short as I could
get it).

Why do the first three calls to getMsg() work,
while the calls to err() fail?

Thanks!

/**************************************************************************/

#include <stdio.h>
#include <varargs.h>

#define SIZE 512

char *msg_array[] = {
    "This is message #1, int=%d, string=%s",
    "msg two, only a float=%f",
    "third one, string=%s, int=%d, long=%ld",
};
static char message[SIZE];

/**************************************************************************/

char *getMsg (va_alist)
    va_dcl /* msg_id, ... */
{
    va_list args;
    int msg_id;

    va_start (args);
    msg_id = va_arg (args, int);

    vsprintf (message, msg_array[msg_id], args);
    va_end (args);

    return (message);
}

/**************************************************************************/

void err(va_alist)
    va_dcl /* app, msg_num, ... */
{
    va_list args;
    char *cp, *ret, *app;
    int num;

    va_start (args);
    app = va_arg (args, char *);

    num = va_arg (args, int);
    ret = getMsg (num, args);

    va_end (args);

    printf ("%s: %s\n", app, ret);
}

/**************************************************************************/

main () {
    printf ("%s\n", getMsg(0, 37, "this is the string"));
    printf ("%s\n", getMsg(1, 37.37));
    printf ("%s\n", getMsg(2, "foo", 37, 37L));

    err ("app1", 0, 37, "this is the string");
    err ("app2", 1, 37.37);
    err ("app3", 2, "foo", 37, 37L);
}

/**************************************************************************/

p.s.  Anyone got any varargs macros, portable between
      traditional varargs and ANSI stdarg?
      Wanna share them?

p.p.s.  Thanks for helping!
--
Kenneth H. Cox
Viewlogic Systems, Inc.
kenstir@viewlogic.com
...!harvard!cg-atla!viewlog!kenstir

chris@mimsy.umd.edu (Chris Torek) (11/04/90)

In article <1990Nov2.170614@madmax.Viewlogic.COM>
kenc@madmax.Viewlogic.COM (Kenstir) writes:
>I passed [a variable] argument list to a function, took a couple of
>parameters off, then tried to pass the rest of the argument list down
>to another function. ...

This can be done, but not the way you think.  getMsg is correct:

[Apologies for the long quotes; I have squished them vertically.  I
 do not recommend coding in this style.]
>char *getMsg(va_alist) va_dcl { va_list args; int msg_id;
>    va_start(args); msg_id = va_arg(args, int);
>    vsprintf(message, msg_array[msg_id], args);
>    va_end(args);
>    return (message);
>}

Note well the fact that getMsg (which is correct and works) must
(and does) call vsprintf, not sprintf.  That `v' is important.

The next function, however, is wrong:

>void err(va_alist) va_dcl { va_list args; char *cp, *ret, *app; int num;
>    va_start(args); app = va_arg(args, char *);
>    num = va_arg(args, int);
>    ret = getMsg(num, args);

Here is the erroneous line.  How do you expect getMsg to work when you
had to call vsprintf?  If getMsg was good enough to do the trick, why
would vsprintf exist at all?

The fix, then, is to write a `vgetMsg' function.  In the process it
is simplest to rewrite getMsg in terms of vgetMsg:

	vgetMsg(msg_id, ap) int msg_id; va_list ap; {
		(void) vsprintf(message, msg_array[msg_id], args);
		return (message);
	}
	getMsg(va_alist) va_dcl {
		va_list args; int msg_id; char *ret;
		va_start(args); msg_id = va_arg(args, int);
		ret = vgetMsg(msg_id, args); va_end(args);
		return (ret);
	}

Now change the last quoted line of `err' to

	ret = vgetMsg(num, args);


>p.s.  Anyone got any varargs macros, portable between
>      traditional varargs and ANSI stdarg?

If you mean what I think you mean, it cannot be done.  The reason
is that `va_start' has the same name in <varargs.h> and <stdarg.h>
but the former version (used above) has only one parameter while the
latter has two.  It is thus impossible to write a single implementation.

If you mean the other thing I think you mean, it can be done, but it
looks horrible (and I will not even give an example).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

lerman@stpstn.UUCP (Ken Lerman) (11/06/90)

In article <27416@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
...
[->> has lost attribution.]
->>p.s.  Anyone got any varargs macros, portable between
->>      traditional varargs and ANSI stdarg?
->
->If you mean what I think you mean, it cannot be done.  The reason
->is that `va_start' has the same name in <varargs.h> and <stdarg.h>
->but the former version (used above) has only one parameter while the
->latter has two.  It is thus impossible to write a single implementation.
->
->If you mean the other thing I think you mean, it can be done, but it
->looks horrible (and I will not even give an example).
->-- 
->In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
->Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

I generally put the following in my code (by #include):

#if defined(__STDC__) && __STDC__ != 0
#include <stdarg.h>
#define VA_START(ap,last) va_start(ap,last)
#else
#include <varargs.h>
#define VA_START(ap,last) va_start(ap)
#endif

Then I can write VA_START(ap, lastArg) in my code as if I'm using the
ANSI style of va_start.

While this is not a single va_start which does both, I think it meets
the portablility goals which were stated.

Ken