20100307

std::string and sprintf

Amongst the many projects I am currently juggling, one of them involves developing some C++ code for an embedded Linux project. I've just hit an instance where I'd like to be able to use “printf” style formatting using std::string instances, but it can be pretty clumsy – I need to allocate buffer space for sprintf, invoke the function, and package up the result into another string.

I did a quick search on the Internet and found many others looking for the same thing: “Is there a way I can use sprintf in c++ using std:strings?” And invariably the answers were either


  • “No”

  • “Use std::ostringstream and c++'s built-in manipulators”

  • “Use boost::format

And they are all right!

It turns out that you can't safely use the *printf style variable argument lists with C++, as only POD (Plain Old Data) types can be passed in the list. Attempting to pass most other types will result in a segmentation fault at run time. The official C++ way is to use ostringstream and the usual stream manipulators to format parameters, but for me, that make's writing tests more difficult because I need to “build” the expected streams exactly the same way in my test code. The final method could work for me, except that this is an embedded project and I am trying to keep the number of libraries referenced to a minimum, since I need to build and include them on my target Linux system.

So my solution was to accept the limitations of the printf function and write a wrapper function that hides the buffer setup and string creation:

#include <string>
#include <stdarg.h>
#include <stdio.h>

string FormatString(const string& format, ...) {
char buffer[1024];

va_list arglist;
va_start(arglist, format);
int length = vsnprintf(buffer, sizeof(buffer), format.c_str(), arglist);
va_end(arglist);

return string(buffer, length);
}



I've tucked this away in a nested namespace so I can invoke it easily when needed. Now when I need formatted text I can just supply a c string or std::string format and my parameters, and get back a nice std::string instance in return. But there's a catch! I can't pass in a std::string as a parameter because the old-school c variable argument list can't handle objects. If I try to do that, I will get a “warning: cannot pass objects of non-POD type 'struct std::string' through '...'; call will abort at runtime” warning and a segmentation fault at runtime. The work around (at least for std::string objects) is to invoke the c_str() method on the string parameter instance. So for example

    string argument = "a string instance";
string result = FormatString("I can format a %s parameter!", argument.c_str());


allows me to supply std::string arguments as well. The c_str() function returns a pointer to a temporary buffer containing a c style string version of the string object's value.

So now I have my template style formatting and I can move on with my project. This project is my first foray into any substantial C++ programming, so if you seen any glaring problems or know a better solution, or if this little tip helped you too, please let me know by leaving a comment.

Read More......
 
Template design by Amanda @ Blogger Buster