From: ecm@casbah.acns.nwu.edu (Edward Malthouse)
Newsgroups: comp.lang.c++
Subject: varargs
Date: 10 Jan 93 19:36:17 GMT
Organization: Northwestern University, Evanston Illinois.


I am trying to write a function error which will will be declared as follows:
error(va_list)

The usage will be
error(char *format, arg_list)
so that it writes to stderr the arguments that comprise the arg_list under the
control of the string pointed to by format.  The string pointed to 
by format is to have the same form as that used by the printf
family of functions.  After the message is printed, error calls the
cleanup function and then exits with an error status of 1.  The
cleanup function must be provided by the user and should be declared as
void cleanup(void); The purpose of cleanup is to return any global resourses 
allocated at runtime and close any global files that may be open.

I've used varargs for other applications but am having a few problems with
this one.  I've tried three different approaches, but none work
satisfactorily.  The approaches are as follows:

void error(va_alist)
va_dcl
{
    va_list ap;
    char *format;

    va_start(ap);
    format = va_arg(ap, char*);
    fprintf(stderr,format,ap);    
/*    fprintf(stderr,format,&ap);    */
    va_end(ap);
    cleanup();
    exit(1);
} /* error */

I get a core dump with the first one when fprintf tries to pop anything off 
of ap (or &ap).

void error(va_alist)
va_dcl
{
    va_list ap;
    char *t, *u;
    char *format;
    short argcnt;       /* counts number of %*'s in the format string */
    char *argptr;
    char buffer[100];

    va_start(ap);
    format = va_arg(ap, char*);
    for(t=format; *t; t++)
        if(*t == '%') /* print a % */
            if(*(t+1) == '%') fputc(*t++,stderr);
            else{
                /* cut format argument */
                u = buffer;
                do
                    *u++ = *t++;
                while(isdigit(*t) || *t=='.');
                *u = *t;
                *(u+1) = '\0';
                argptr = va_arg(ap, void*);
                fprintf(stderr,buffer,*argptr);
/*                fprintf(stderr,buffer,argptr);  */
            } /* else */
        else fputc(*t,stderr);

    va_end(ap);
    cleanup();
    exit(1);
} /* error */

The second one will not compile when I send *argptr.  When argptr is sent it
core dumps.

void error(va_alist)
va_dcl
{
    va_list ap;
    char *t, *u;        /* loop vars */
    char *format;       /* will point to format str from usr */
    short argcnt;       /* counts number of %*'s in the format string */
    char *tmps, tmpc;
    double tmpd;
    int tmpi;
    char buffer[100];

    va_start(ap);
    format = va_arg(ap, char*);
    for(t=format; *t; t++)
        if(*t == '%') /* print a % */
            if(*(t+1) == '%') fputc(*t++,stderr);
            else{
                /* cut out format arg */
                u = buffer;
                do
                    *u++ = *t++;
                while(isdigit(*t) || *t=='.');
                *u = *t;
                *(u+1) = '\0';

                switch(*u){
                case 'c':  tmpc = va_arg(ap, char);
                    fprintf(stderr,buffer,tmpc);                break;
                case 'd':
                case 'i':
                case 'o':
                case 'x':  tmpi = va_arg(ap, int);
                    fprintf(stderr,buffer,tmpi);                break;
                case 'e':
                case 'f':
                case 'g':  tmpd = va_arg(ap, double);
                    fprintf(stderr,buffer,tmpd);                break;
                case 's':  tmps = va_arg(ap, char*);
                    fprintf(stderr,buffer,tmps);                break;
                default:  fprintf(stderr,"\nunrecognized option (%c)\n",*u);
                    continue;
                } /* switch */
            } /* else */
        else fputc(*t,stderr);

    va_end(ap);
    cleanup();
    exit(1);
} /* error */

This version works but I am concerned about portability problems when it
comes to all the different flavors of int's (int, short, long, unsigned,
etc.), doubles, floats, etc.  I have not included any code to handle the
backslash codes or formats with *'s in them (e.g., printf("%*d\n",10,i)) yet.

My questions are as follows:

-  Method 1 seems like the best way.  Is there any way of getting this to
   work?

-  How does printf etc. get around the "multiple flavors of int's" problem,
   i.e., printf does not know if a %d is associated with a short, int, long,
   unsigned long int, etc.  How does it know what type to specify when it
   calls va_arg?

Thank you in advance for your help.  Please mail any responses and I will
summarize if there is interest.

Ed Malthouse
Department of Statistics
Northwestern University
Newsgroups: comp.lang.c++