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:
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
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.
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
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 = va_arg(arg_list, 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 = va_arg(arg_list, void );
int conv;
conv = sscanf(str, format, p);
convtot += conv;
}
free(nformat);
return convtot;
}
int vscanf(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 = va_arg(arg_list, 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 = va_arg(arg_list, 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 = va_arg(arg_list, 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 = va_arg(arg_list, void );
int conv;
conv = fscanf(fd, format, p);
convtot += conv;
}
free(nformat);
return convtot;
}
Add Comment