phil123456
phil123456

Reputation: 1

C++ - porting linux code - _vscprintf returns wrong number of arguments

A while ago I wrote a logger class under linux and I try to port it under windows (visual studio) and it keeps crashing

first I had to use vsprintf_s intead of vsprintf, cos the comiler rejected it

but the only example I found does not seem to work

"len" contents wrong values instead of real number of arguments

here is how I call my function :

Logger::print("Rock and roll");

or

Logger::print("%4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n", a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y);

original linux code:

void Logger::print(const std::string fmt,...)
{
    char formatted_string[2*1024];
    strcpy(formatted_string,this->prefix);
    strcat(formatted_string,"  ");

    char*strPtr=&formatted_string[0]+strlen(this->prefix)+1;

    va_list argptr;
    va_start(argptr, fmt );

    vsprintf(strPtr,fmt.c_str(), argptr);
    va_end(argptr);

    printf("%s\n",formatted_string);
}         

new windows code:

void Logger::print(const std::string fmt, ...)
{
    va_list args;
    int len;
    char * buffer;
    va_start(args, fmt);
    len = _vscprintf(fmt.c_str(), args); << len contents odd values instead of real number of arguments
    va_end(args);
    buffer = (char*)malloc(len * sizeof(char));
    vsprintf_s(buffer, len, fmt.c_str(), args);
    Logger::file << buffer;
    free(buffer);
}

regards

Upvotes: 0

Views: 316

Answers (2)

WhozCraig
WhozCraig

Reputation: 66254

You're prematurely invoking va_end. Fixing that, you're also not calculating the correct target size, as result of _vscprintf does not include space for the terminating null characters, which vsprintf_s will utilize.

The body of your function should look like this, including removing that malloc call in favor of a proper RAII solution using a vector:

va_list args;
va_start(args, fmt);

int len = _vscprintf(fmt.c_str(), args);
if (len > 0)
{
    std::vector<char> buff(len + 1); // include terminator space
    vsprintf_s(&buff[0], buff.size(), fmt.c_str(), args);
    Logger::file << buff.data();
}
va_end(args);

Worth noting: running your original code in the debugger will result in a buffer-too-small exception, which the debugger will catch handily. Debuggers are what's for dinner.

Upvotes: 1

phil123456
phil123456

Reputation: 1

here is the working code

void Logger::print(const std::string fmt, ...)
{
    va_list args;
    int len;
    char * buffer;
    va_start(args, fmt);
    len = _vscprintf(fmt.c_str(), args)+1;
    buffer = (char*)malloc(len * sizeof(char));
    vsprintf_s(buffer, len, fmt.c_str(), args);
    va_end(args);
    Logger::file << buffer;
    free(buffer);
}

Upvotes: 0

Related Questions