Thread: I'll have a Variable argument list to go...

  1. #1
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273

    Lightbulb I'll have a Variable argument list to go...

    Hello,

    As I've now realised using scanf is a false economy (For want of a better word :P), I've been trying to encapsulate fgets and sscanf into a single function. My only problem with this is that after remembering the declaration for scanf uses the evil ellipsis (...), signifying a bevvy of arguments that the system never has much idea as to the length of, I screamed "NOOOOOOOOOOOOO!" and rammed my fist into my keyboard, snapping it in two. Anyways, a new keyboard later...

    I've had a go at making the function, like so:-
    Code:
    int ReadInput(char *szOutput, int iSize, const char *szInput, ...)
    {
    	const char *pTemp;
    	va_list ap;
    	void *args = NULL;
    	unsigned long *arg;
    	int iArgs = 0;
    
    	va_start(ap, szInput);
    	fgets(szOutput, iSize, stdin);
    	pTemp = szInput;
    	while (*pTemp != '\0')
    	{
    		if (*(pTemp++) == '%')
    		{
    			args = realloc(args, (iArgs + 1) * sizeof(void *));
    			arg = (unsigned long *)args + iArgs++ * sizeof(void *);
    			*arg = (unsigned long)va_arg(ap, void *);
    		}
    
    	}
    
    	va_end(ap);
    	sscanf(szOutput, szInput, args);
    	free(args);
    
    	return 0;
    }
    The variable arguments of ReadInput should be the same as that of sscanf, so they are all addresses to variables, hence me attempting to collect the addresses together and present them to sscanf myself. This doesn't work hoewever as "args" is now doubt treated as an argument in itself...

    Is there perhaps a low-level method of pushing all the arguments in with scanf, or am I stuck until I get hold of a C99-compliant compiler?

  2. #2
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    Hmm... what exactly are you trying to do? Can you give an example of how you'd call this function and what you'd expect if to do for you.
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  3. #3
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    There is an easy way to do this, and there is a hard way. Unfortunately, the easy way is only possible with C99 compliant compilers that support the vsscanf function:
    Code:
    #include <stdio.h>
    #include <stdarg.h>
    
    /*
    ** This function requires C99 compliance.
    */
    int readConsoleInput ( char *buf, size_t size, char *fmt, ... )
    {
      int nitems;
      va_list args;
    
      if ( fgets ( buf, size, stdin ) == NULL )
        return EOF;
    
      va_start ( args, fmt );
      nitems = vsscanf ( buf, fmt, args ); /* C99 invented function */
      va_end ( args );
    
      return nitems;
    }
    Otherwise you're effectively on your own when it comes to scanning in values from the buffer.

    -Prelude
    My best code is written with the delete key.

  4. #4
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273
    Originally posted by Hammer
    Hmm... what exactly are you trying to do? Can you give an example of how you'd call this function and what you'd expect if to do for you.
    Naturally I want it to act as a "safe" version of scanf, in that it has defined buffer limitations, by combining fgets and sscanf into a single function. What I need to do is having supplied the addresses for variables to ReadInput in a variable argument list, transfer them to scanf.

    The people at #c on EFnet said exactly the same thing, Prelude

    C'mon, I'm certain there's a way of doing this at or somewhere close to assembler level...

  5. #5
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >C'mon, I'm certain there's a way of doing this at or somewhere close to assembler level...
    Very nasty, very untested, but this is a relatively simple way of getting what you want. It's not nearly as safe as the C99 alternative I gave you earlier, but it should. Also note that I didn't add all of the format flags, if you need them they shouldn't be diffiult to add:
    Code:
    #include <stdio.h>
    #include <stdarg.h>
    
    static int indices[511];
    static char *formats[] = {
      "",
      "%hd","%hi","%ho","%hu","%hx","%hn", /* Short int   */
      "%d", "%i", "%o", "%u", "%x", "%n",  /* int         */
      "%ld","%li","%lo","%lu","%lx","%ln", /* long int    */
      "%c", "%s",                          /* char        */
      "%e", "%f", "%g",                    /* float       */
      "%le","%lf","%lg",                   /* double      */
      "%Le","%Lf","%Lg",                   /* long double */
      NULL,
    };
    
    static void initIndices ( void )
    {
      char **p;
      int mark = 1;
    
      for ( p = formats + 1; *p != NULL; p++ ) {
        if ( (*p)[2] != '\0' )
          indices[(*p)[1] + (*p)[2]] = mark++;
        else
          indices[(*p)[1]] = mark++;
      }
    }
    
    int readConsoleInput ( char *buf, size_t size, char *fmt, ... )
    {
      char *p, *fstr;
      int n = 0, nitems = 0;
      va_list args;
    
      if ( fgets ( buf, size, stdin ) == NULL )
        return EOF;
    
      initIndices();
      va_start ( args, fmt );
    
      for ( p = fmt; *p != '\0'; p++ ) {
        if ( *p != '%' )
          continue;
    
        switch ( *++p ) {
        case 'h':
          fstr = formats[indices[p[0] + p[1]]];
          n += sscanf ( buf, fstr, va_arg ( args, short int * ) );
          break;
        case 'l':
          fstr = formats[indices[p[0] + p[1]]];
          if ( p[1] == 'e' || p[1] == 'f' || p[1] == 'g' )
            n += sscanf ( buf, fstr, va_arg ( args, double * ) );
          else
            n += sscanf ( buf, fstr, va_arg ( args, long int * ) );
          break;
        case 'L':
          fstr = formats[indices[p[0] + p[1]]];
          n += sscanf ( buf, fstr, va_arg ( args, long double * ) );
          break;
        case 'd': case 'i': case 'o': case 'u': case 'x': case 'n':
          fstr = formats[indices[p[0]]];
          n += sscanf ( buf, fstr, va_arg ( args, int * ) );
          break;
        case 'c': case 's':
          fstr = formats[indices[p[0]]];
          n += sscanf ( buf, fstr, va_arg ( args, char * ) );
          break;
        case 'e': case 'f': case 'g':
          fstr = formats[indices[p[0]]];
          n += sscanf ( buf, fstr, va_arg ( args, float * ) );
          break;
        default:
          /* Ignore */
          break;
        }
    
        if ( n == EOF )
          return EOF;
        
        nitems += n;
      }
    
      va_end ( args );
    
      return nitems;
    }
    -Prelude
    My best code is written with the delete key.

  6. #6
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    SMurf, might your compiler have implemented a vsscanf as a pre-C99 language extension?
    Code:
    #include <stdio.h>
    #include <stdarg.h>
    #include <string.h>
    
    #if defined(__STDC_VERSION__) && (__STDC_VERSION__ == 199901L)
    #error "C99"
    #else
    int ReadInput ( char *buffer, size_t size, const char *format, ... )
    {
      int items;
      va_list arglist;
    
      if ( fgets ( buffer, size, stdin ) != NULL )
      {
          char *newline = strchr(buffer, '\n');
          if(newline != NULL)
          {
              *newline = '\0';
          }
          else
          {
              while(getchar() != '\n');
          }
      }
      else
      {
          return EOF;
      }
    
      va_start ( arglist, format );
      items = vsscanf ( buffer, format, arglist );
      va_end ( arglist );
    
      return items;
    }
    #endif
    
    int main(void)
    {
        char buffer[8];
        int i,j;
        double k;
        fputs("Enter two integers: ", stdout);
        fflush(stdout);
        if ( ReadInput(buffer, sizeof(buffer), "%d%d", &i, &j) == 2 )
        {
            printf("buffer = \"%s\", i = %d, j = %d\n", buffer, i, j);
        }
        fputs("Enter a double: ", stdout);
        fflush(stdout);
        if ( ReadInput(buffer, sizeof(buffer), "%lf", &k) == 1 )
        {
            printf("buffer = \"%s\", k = %f\n", buffer, k);
        }
        return 0;
    }
    Code:
    C:\Test>C:\Test>make Test.exe
    MAKE Version 5.0  Copyright (c) 1987, 1997 Borland International
       c:\bc5\bin\bcc32 -P- -c @MAKE0001.@@@
    Borland C++ 5.2 for Win32 Copyright (c) 1993, 1997 Borland International
    test.c:
       c:\bc5\bin\Ilink32 @MAKE0000.@@@
    Turbo Incremental Link  Version 1.0 Copyright (c) 1997 Borland International
    Code:
    C:\Test>Test
    Enter two integers: 1564 2307
    buffer = "1564 23", i = 1564, j = 23
    Enter a double: 3.1415926535897932384626433832795
    buffer = "3.14159", k = 3.141590
    Code:
    C:\Test>Test
    Enter two integers: 12 23
    buffer = "12 23", i = 12, j = 23
    Enter a double: 3.14
    buffer = "3.14", k = 3.140000

  7. #7
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273
    Dave_Sinkula: Heh, Microsoft aren't THAT giving - C99 compliance is a feature of VC++.NET, not VC++ 6 (Which is what I'm using), as I'm sure that by now they've run out of things they can add to it...

    Prelude: Hmm, I see your point of view, but seeing as all pointers are effectively unsigned longs on 32-bit platforms, couldn't I cut a corner, not parse each individual format type and simply detect each format specifier in the input string and generate a sscanf for each one, with va_arg(args, unsigned long) as the argument?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help sorting a linked list. Beginner
    By scarlet00014 in forum C Programming
    Replies: 1
    Last Post: 09-27-2008, 06:16 PM
  2. help! Placement of nodes in a Linked List
    By lostmyshadow in forum C Programming
    Replies: 6
    Last Post: 12-17-2007, 01:21 PM
  3. How can I traverse a huffman tree
    By carrja99 in forum C++ Programming
    Replies: 3
    Last Post: 04-28-2003, 05:46 PM
  4. problem with structures and linked list
    By Gkitty in forum C Programming
    Replies: 6
    Last Post: 12-12-2002, 06:40 PM
  5. singly linked list
    By clarinetster in forum C Programming
    Replies: 2
    Last Post: 08-26-2001, 10:21 PM