Pladnius Brooks
Pladnius Brooks

Reputation: 1308

Trouble with logfile output - difference between string and c_str - C++

I am trying to finalize my logging class. I have written it from scratch and do not wish to use an alternative library of any kind. My problem lies within the fact that my logger has trouble outputting std::strings and only works when I denote it with the string.c_str() function.

Here is my logfile output function:

void Log::writeSuccess(char * text,...)
{
    // Grab the variables and insert them
    va_list ap;
    va_start(ap, text);
    char buff[BUFFER_SIZE];
    vsnprintf(buff, sizeof(buff), text, ap);

    // Output to the log
    logfile << "<-!-> " << buff << endl;
}

Here is a sample call to my log class object (ignore the uselessness of the call):

string test("This is a test string!");
errorLog.writeSuccess("Output: %s", test);

I end up with random characters and garbled output.

However, when I append the string, test with .c_str(), it outputs the text correctly.

The whole reason I am trying to avoid cstrings is because I understand they are not cross platform and am developing my client to support all the major operating systems.

To summarize:

  1. What is wrong with my log output function? Do you see any way it could be improved?

  2. Should I generally avoid c_strings?

Upvotes: 1

Views: 368

Answers (2)

Dialecticus
Dialecticus

Reputation: 16779

When calling function with variable parameters you must use simple types. For string you must use c_str(). There's no workaround. MFC's CString is designed so that you can get away with using it directly, but that was Microsoft's decision, and part of their design.

EDIT: As I said when calling function with variable parameters you must use string::c_str(). However, instead of C-like functions with variable parameters you can use something like boost::format() and it's parameter feeding operator %. This also gives you more control over ordering of parameters, which is very handy for i18n.

Upvotes: 1

AusCBloke
AusCBloke

Reputation: 18522

You're getting random garble when passing an std::string to vsnprintf because the format specifier "%s" is that of a C-string - a char*.

std::string is not of type char*, but std::string.c_str() is of type char*. vsnprintf will basically read chars pointed to by the address that it presumes is that of a start of a C-string, up until the NUL character '\0'.

An std::string pushed onto the stack and passed as an argument to vsnprintf is not a pointer to a char, however vsnprintf will just treat these bytes as an address and start reading chars/bytes from this address, causing undefined behaviour.

The printf family of functions are not typesafe, since they rely on a format string and variable argument list, which is why your code will compile but you'll get unexpected results.


Bottom line is the printf family of functions expect a char* when you use the format specifier "%s".

I also think you're confusing C style strings (char[]) with the Microsoft-specific CString class. C style strings won't cause you problems on different platforms at all; the literal "This is a test string!" is a C style string (const char[]).

Upvotes: 2

Related Questions