Sign up ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm working on a roguelike and attempting to write the message system. When I attempt to format a const char* with vsnprintf, it seems to somehow format it incorrectly to the point where it is corrupted.

void Gui::render()
{
    for(auto iter = log.begin(); iter != log.end(); iter++)
    {
        terminal_printf(1, 45, (*iter));
    }
}

void Gui::message(const char *text, ...)
{
    char buffer[256];
    va_list argptr;
    va_start(argptr, text);
    vsnprintf(buffer, 256, text, argptr);
    va_end(argptr);
    log.emplace(log.begin(), buffer);
}

If I call puts() in message() with buffer as an argument after it has been formatted, it is correctly formatted. If I directly emplace the text, it isn't corrupted, but it obviously isn't formatted. If puts is called in the iteration loop on the iterator, it outputs the corrupted data.

share|improve this question

closed as off-topic by msell, Josh Petrie Sep 6 at 19:01

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Programming questions that aren't specific to game development are off-topic here, but can be asked on Stack Overflow. A good rule of thumb is to ask yourself "would a professional game developer give me a better/different/more specific answer to this question than other programmers?"" – msell, Josh Petrie
If this question can be reworded to fit the rules in the help center, please edit the question.

1 Answer 1

Stop using format functions in C++

TL;DR: They are unsafe, vulnerable to all sorts of buffer overrun exploits and a pain to debug when you make a mistake.

std::cout and the standard C++ streams are much more robust than printf and the C IO library and formatting functions. Format strings tend to be more readable and there is some value in separating the data from the presentation, but unfortunately, they are a C mechanism that is incompatible with C++'s object model. So you cannot, for instance, pass a C++ class type to one of those functions (don't even think about passing it a std::string!).

Format strings are also not type safe, since you wave compile-time type inference and rely on manually matching the type of each parameter with the %s in the format string. In the end, that's a recipe for disaster and these issues outweigh any gains in readability. Having to manually specify the type in the format, when the compiler always knows the type of the variable passed in, is also a violation of the DRY principle.

Better options exist, beside the C++ streams, if you really don't dig the << operator syntax:


That said, let's look at your code. What could be wrong?

It could be a buffer overflow in Gui::message(). The buffer is sized to 256 characters, if the string is longer than that, you run into trouble.

What you can do is increase the buffer size and test. Also, make sure to manually terminate the string, just in case:

char buffer[1024];
va_list argptr;

va_start(argptr, text);
vsnprintf(buffer, sizeof(buffer), text, argptr);
va_end(argptr);

buffer[sizeof(buffer) - 1] = '\0';

This code also seems suspicious:

void Gui::render()
{
    for(auto iter = log.begin(); iter != log.end(); iter++)
    {
       terminal_printf(1, 45, (*iter));
    }
}

There's not enough information here to be certain, but what are the contents of log? chars, strings?

If (*iter) is returning a char, then the correct way to print it with a format is:

terminal_printf(1, 45, "%c", (*iter));

If it contains a char * string (array of chars), the correct print call is:

terminal_printf(1, 45, "%s", (*iter));

Assuming the signature of terminal_printf is something like:

void terminal_printf(int, int, const char*, ...);
share|improve this answer
    
Sorry, log is an std::vector<const char*>. Manual termination seems to have no effect. terminal_printf() is a library function that takes x and y coordinates with a const char* and prints it at the specified location. Even passing (*iter) as an argument to printf with a "%s" doesn't work. This may be a library issue, but I don't see why it works properly before the format with vsnprintf and fails to output correctly after it is formatted. –  user71842 Sep 6 at 19:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.