Visual C++ and vsscanf

Posted by Kaya Kupferschmidt • Wednesday, February 1. 2006 • Category: C++
When it comes to its IDE and its compiler, I consider Microsoft Visual C++ 7.1 and up to be a very good product. Especially the C++ language compatibility is at a very high level. But when it comes to its included standard C library, its compatibility even has gone worse with the 8.0 release, and its C99 compliance is also very liited.

This fact can you get into troubles earlier than one might guess. When you want to write your own string classes (and also in some other cases), it is useful to have a method with a variable number of arguments that in turn calls sprintf or sscanf (which both accept a variable number of arguments). Image the following:

class String {
public:
   int sprintf(const char* fmt,...)

private:
   char* _Buffer;
}

int String::sprintf(const char* fmt, ...) {
   va_list arglist;
   va_start(arglist, fmt);
   return sprintf(_Buffer, fmt, arglist);
}


When you try to run this code, you will quickly find out that it does not work. The reason is that passing a va_list object as an argument actually is something quite different from passing a variable number of arguments. Luckily the C99 standard offers a solution to that problem by including a new vsprintf function that takes a va_list as an argument. So the code above has to be rewritten as

class String {
public:
   int sprintf(const char* fmt,...)

private:
   char* _Buffer;
}

int String::sprintf(const char* fmt, ...) {
   va_list arglist;
   va_start(arglist, fmt);
   return vsprintf(_Buffer, fmt, arglist);
}

This code performs as intended. But what about a similar code for sscanf? The C99 standard includes the function
vsscanf
for a similar implementation of a String::sscanf(const char* fmt,...) method, but this valuable C function is actually missing from the Microsoft C library!

Actually there has been several workarounds available, but I really don't like them all. Now I offer another solution that is much more "native" than the workarounds mentioned before: I simply took the sources of the Microsoft C library (freely available in the Microsoft Platform SDK) and changed one source file in order to get a working and compatible version of vsscanf.

Of course, there is no free lunch at Microsoft: You are allowed to use the source code in your own projects, but you have to pay attention to the constraints imposed by the license from Microsoft. Especially this license does not allow the usage of the code a) on platforms other than Microsoft Windows and b) in any GPL project (BSD or MIT style licenses are okay). For GPL projects, you also can simply grab the vsscanf implementation found in the GNU C library.

Download source of vsscanf.

2 Comments

Display comments as (Linear | Threaded)
  1. *I just needed a vsscanf() function and I used another solution: I just cut the format string into as many parts as there are variables to be decoded. I also made vscanf() and vfscanf() versions of it but, to be honnest, I didn't test them! vsscanf() works well, however.

    Sorry for the comments in French!

    A nice improvement would be if there was a way to tell the compiler that it should check the matching between the format and the types of the variables as it does (or does it really?) for scanf.

    include "stdafx.h"

    include

    include

    include

    include

    int vsscanf(const char str, const char *p_format, va_list arg_list){ // fonction qui curieusement n'existe pas chez Microsoft char *nformat = strdup(p_format); char *format = nformat; int i; int n=0; int convtot=0; for (i=0; format[i]; i++){ if (format[i] == '%'){ if (format[i+1] == '%' || format[i+1] == '' || format[i+1] == '\0') i++; // pas compté comme variable à allouer, je saute else { // c'est une variable if (n==0) // première variable: OK n++; else { // deuxième variable: on coupe juste avant char f[3]; f[1]=format[i+1]; f[2]=format[i+2]; format[i+1]='n'; format[i+2]='\0'; // %n = nombre de caractères lus dans l'entrée void p = vaarg(arglist, void ); int lu=-1,conv; conv = sscanf(str, format, p, &lu); if (lu==-1){ // échec à la conversion convtot += conv; // peut-être converti quand même une variable? break; // mais ensuite on arrête } convtot++; // une variable lue str += lu; // avancer dans l'entrée format[i+1]=f[1]; format[i+2]=f[2]; // rétablir la chaîne de format format += i; i = -1; // avancer dans le format n=0; // on repart avec 0 variable détectée dans la nouvelle chaîne } } } } if (format[i]=='\0' && n>0) { // on est arrivé au bout, et il reste une variable à convertir void p = vaarg(arglist, void ); int conv; conv = sscanf(str, format, p); convtot += conv; } free(nformat); return convtot; }

    int vscanf(const char pformat, valist arglist){ // fonction qui curieusement n'existe pas chez Microsoft char *nformat = strdup(pformat); char *format = nformat; int i; int n=0; int convtot=0; for (i=0; format[i]; i++){ if (format[i] == '%'){ if (format[i+1] == '%' || format[i+1] == '' || format[i+1] == '\0') i++; // pas compté comme variable à allouer, je saute else { // c'est une variable if (n==0) // première variable: OK n++; else { // deuxième variable: on coupe juste avant char f[3]; f[1]=format[i+1]; f[2]=format[i+2]; format[i+1]='n'; format[i+2]='\0'; // %n = nombre de caractères lus dans l'entrée void p = vaarg(arglist, void ); int lu=-1,conv; conv = scanf(format, p, &lu); if (lu==-1){ // échec à la conversion convtot += conv; // peut-être converti quand même une variable? break; // mais ensuite on arrête } convtot++; // une variable lue // str += lu; // avancer dans l'entrée format[i+1]=f[1]; format[i+2]=f[2]; // rétablir la chaîne de format format += i; i = -1; // avancer dans le format n=0; // on repart avec 0 variable détectée dans la nouvelle chaîne } } } } if (format[i]=='\0' && n>0) { // on est arrivé au bout, et il reste une variable à convertir void p = vaarg(arglist, void ); int conv; conv = scanf(format, p); convtot += conv; } free(nformat); return convtot; }

    int vfscanf(FILE fd, const char *p_format, va_list arg_list){ // fonction qui curieusement n'existe pas chez Microsoft char *nformat = strdup(p_format); char *format = nformat; int i; int n=0; int convtot=0; for (i=0; format[i]; i++){ if (format[i] == '%'){ if (format[i+1] == '%' || format[i+1] == '' || format[i+1] == '\0') i++; // pas compté comme variable à allouer, je saute else { // c'est une variable if (n==0) // première variable: OK n++; else { // deuxième variable: on coupe juste avant char f[3]; f[1]=format[i+1]; f[2]=format[i+2]; format[i+1]='n'; format[i+2]='\0'; // %n = nombre de caractères lus dans l'entrée void p = vaarg(arglist, void ); int lu=-1,conv; conv = fscanf(fd, format, p, &lu); if (lu==-1){ // échec à la conversion convtot += conv; // peut-être converti quand même une variable? break; // mais ensuite on arrête } convtot++; // une variable lue // str += lu; // avancer dans l'entrée format[i+1]=f[1]; format[i+2]=f[2]; // rétablir la chaîne de format format += i; i = -1; // avancer dans le format n=0; // on repart avec 0 variable détectée dans la nouvelle chaîne } } } } if (format[i]=='\0' && n>0) { // on est arrivé au bout, et il reste une variable à convertir void p = vaarg(arglist, void ); int conv; conv = fscanf(fd, format, p); convtot += conv; } free(nformat); return convtot; }

  2. *Thanks for your comment (although the code didn't survive very well). It is a nice idea indeed you have ther. Plus it has no legal questions attached :-)

Add Comment


Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA 1CAPTCHA 2CAPTCHA 3CAPTCHA 4CAPTCHA 5


Markdown format allowed



A Simple Sidebar