I'm writing a small proof-of-concept console program with Visual Studio 2008 and I wanted it to output colored text for readability. For ease of coding I also wanted to make a quick printf-replacement, something where I could write like this:
MyPrintf(L"Some text \1[bright red]goes here\1[default]. %d", 21);
This will be useful because I also build and pass strings around in some places so my strings will be able to contain formatting info.
However I hit a wall against wsprintf because I can't find a function that would allow me to find out the required buffer size before passing it to the function. I could, of course, allocate 1MB just-to-be-sure, but that wouldn't be pretty and I'd rather leave that as a backup solution if I can't find a better way.
Also, alternatively I'm considering using std::wstring (I'm actually more of a C guy with little C++ experience so I find plain-old-char-arrays easier for now), but that doesn't have anything like wsprintf where you could build a string with values replaced in them.
So... what should I do?
Your question is tagged C++, in which case I'd say std::wstringstream is the way to go. Example:
#include <sstream>
void func()
{
// ...
std::wstringstream ss; // the string stream
// like cout, you can add strings and numbers by operator<<
ss << L"Some text \1[bright red]goes here\1[default]. " << 21;
// function takes a C-style const wchar_t* string
some_c_function(ss.str().c_str()); // convert to std::wstring then const wchar_t*
// note: lifetime of the returned pointer probably temporary
// you may need a permanent std::wstring to return the c_str() from
// if you need it for longer.
// ...
}
You want _snwprintf. That function takes a buffer size, and if the buffer isn't big enough, just double the size of the buffer and try again. To keep from having to do multiple _snwprintf calls each time, keep track of what the buffer size was that you ended up using last time, and always start there. You'll make a few excess calls here and there, and you'll waste a bit of ram now and then, but it works great, and can't over-run anything.
I'd go for a C++ stringstream. It's not as compact as sprintf but it will give you the functionality you want.
If you can afford using boost, you could consider boost::format. It would give you the flexibility of std::strings, and formatting features of sprintf. It is fairly different from C-style, but is also fairly easy to use. Here's an example.
_scprintf, _scprintf_l, _scwprintf, _scwprintf_l
This functions will return the number of characters in the formatted string.
Using std::wstring seems like a good solution if you plan on passing strings between your objects - it handles the size and has a nice c_str method that will give you the array of wide chars.
The additional benefit is that you can pass it by reference instead of by pointer.
When you need the actuall string just use c_str method:
wprintf(L"string %s recieved!", myWString.c_str());
This answer is an expansion of the answer from #mheyman that uses vswprintf().
I also struggled with the same problem. The Microsoft documentation is weak, but this page was helpful: https://en.cppreference.com/w/c/io/vfwprintf
CppRef Description: If bufsz is greater than zero, writes the results to a wide string buffer. At most bufsz-1 wide characters are written followed by null wide character. If bufsz is zero, nothing is written (and buffer may be a null pointer).
CppRef Return value: Number of wide characters written (not counting the terminating null wide character) if successful or negative value if an encoding error occurred or if the number of characters to be generated was equal or greater than size (including when size is zero).
Roughly:
Measure required buffer size by calling vswprintf() with buffer == NULL and bufsz == 0
Call malloc() (or friends) to allocate a buffer.
Again, call vswprintf() with allocated buffer and buffer size + 1
Use result
Call free() on allocated buffer
Your example uses wchar_t: MyPrintf(L"Some text \1[bright red]goes here\1[default]. %d", 21);, so I recommend something like this:
#include <stdio.h> // includes both <wchar.h> and <stdarg.h>
#include <stdlib.h> // calloc()
void MyPrintf(const wchar_t *lpFormatWCharArr,
...)
{
// Ref: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/va-arg-va-copy-va-end-va-start?view=msvc-172
va_list ap;
va_start(ap, lpFormatWCharArr);
// does not include trailing NUL char
const int cch = vswprintf(NULL, // wchar_t *buffer
0, // size_t bufsz
lpFormatWCharArr, // const wchar_t *format
ap); // va_list vlist
va_end(ap);
if (cch < 0)
{
// handle error
}
const size_t NUL_CHAR_LEN = 1;
const size_t buf_len = cch + NUL_CHAR_LEN;
// malloc() is faster, but does not memset() result to zero
wchar_t *buf = calloc(buf_len, // size_t number
sizeof(wchar_t)); // size_t size
if (NULL == buf)
{
// handle error
}
va_list ap2;
va_start(ap2, lpFormatWCharArr);
// does not include trailing NUL char
const int cch2 = vswprintf(buf, // wchar_t *buffer
buf_len, // size_t bufsz
lpFormatWCharArr, // const wchar_t *format
ap2); // va_list vlist
va_end(ap2);
if (cch2 < 0)
{
// handle error
}
if (cch != cch2)
{
// handle error
}
// use 'buf' and 'buf_len'
free(buf);
}
There might be a (code) typo in this answer, but similar code was tested against 64-bit Win 10.
Related
I have a problem with the wcscpy_s function. After wcscpy_s returns the parameter (stringOneand stringTwo) of my function are not readable.
Here is simple demo to show the problem.
void testFunc(LPCWSTR stringOne, LPCWSTR stringTwo) {
wchar_t* defaultVal = L"Default";
wchar_t tmp[100];
int lenBefore = wcslen(stringOne); // Works
auto result = wcscpy_s(tmp, sizeof(tmp), defaultVal);
int len = wcslen(tmp);
int len2 = wcslen(stringOne); // Throws Exception Access violation
}
int main() {
testFunc(L"Test", L"Test");
}
The documentgation of wcscpy_s states that the debug version of this function fills the destination buffer with the special value 0xFE.
When you call wcscpy_s(tmp, sizeof(tmp), defaultVal); you pass the size of the tmp buffer, but wcscpy_s wants the length in number of characters. Therefore the length you pass to wcscpy_s is twice as long as it should be, and as the tmp buffer is overwritten by 0xfe you get a buffer overflow and undefined behaviour, even if the length if the source string (L"Default";) is small.
So use _countof(tmp) instead of _sizeof(tmp).
This said, I suggest you learn how to use the Visual Studio debugger.
As already explained in Michael Walz's answer, you have a buffer overflow caused by passing an incorrect buffer size.
In addition to his suggestion of using _countof(tmp) instead of sizeof(tmp), I'd like to add that in C++ there's a convenient overload of wcscpy_s() that automatically deduces the correct buffer size:
template <size_t size>
errno_t wcscpy_s(
wchar_t (&strDestination)[size],
const wchar_t *strSource
); // C++ only
Basically, you can write simpler code like this, that will just work:
wchar_t tmp[100];
// Use the C++-only template overload of wcscpy_s
// that automatically deduces the destination buffer size
auto result = wcscpy_s(tmp, defaultVal);
If you use this overload, you are immune to those sizeof/_countof mismatch kind of bugs.
Note that this C++ overload works only if you have a static buffer like your wchar_t tmp[100], as the C++ compiler must be able to figure out the buffer size at compile-time. On the other hand, when you have pointers to dynamically-allocated buffers, you have to pass the correct buffer size explicitly.
I've tried implementing a function like this, but unfortunately it doesn't work:
const wchar_t *GetWC(const char *c)
{
const size_t cSize = strlen(c)+1;
wchar_t wc[cSize];
mbstowcs (wc, c, cSize);
return wc;
}
My main goal here is to be able to integrate normal char strings in a Unicode application. Any advice you guys can offer is greatly appreciated.
In your example, wc is a local variable which will be deallocated when the function call ends. This puts you into undefined behavior territory.
The simple fix is this:
const wchar_t *GetWC(const char *c)
{
const size_t cSize = strlen(c)+1;
wchar_t* wc = new wchar_t[cSize];
mbstowcs (wc, c, cSize);
return wc;
}
Note that the calling code will then have to deallocate this memory, otherwise you will have a memory leak.
Use a std::wstring instead of a C99 variable length array. The current standard guarantees a contiguous buffer for std::basic_string. E.g.,
std::wstring wc( cSize, L'#' );
mbstowcs( &wc[0], c, cSize );
C++ does not support C99 variable length arrays, and so if you compiled your code as pure C++, it would not even compile.
With that change your function return type should also be std::wstring.
Remember to set relevant locale in main.
E.g., setlocale( LC_ALL, "" ).
const char* text_char = "example of mbstowcs";
size_t length = strlen(text_char );
Example of usage "mbstowcs"
std::wstring text_wchar(length, L'#');
//#pragma warning (disable : 4996)
// Or add to the preprocessor: _CRT_SECURE_NO_WARNINGS
mbstowcs(&text_wchar[0], text_char , length);
Example of usage "mbstowcs_s"
Microsoft suggest to use "mbstowcs_s" instead of "mbstowcs".
Links:
Mbstowcs example
mbstowcs_s, _mbstowcs_s_l
wchar_t text_wchar[30];
mbstowcs_s(&length, text_wchar, text_char, length);
You're returning the address of a local variable allocated on the stack. When your function returns, the storage for all local variables (such as wc) is deallocated and is subject to being immediately overwritten by something else.
To fix this, you can pass the size of the buffer to GetWC, but then you've got pretty much the same interface as mbstowcs itself. Or, you could allocate a new buffer inside GetWC and return a pointer to that, leaving it up to the caller to deallocate the buffer.
Andrew Shepherd 's answer.
Andrew Shepherd 's answer is Good for me, I add up some fix :
1, remove the ending char L'\0', casue sometime it will trouble.
2, use mbstowcs_s
std::wstring wtos(std::string& value){
const size_t cSize = value.size() + 1;
std::wstring wc;
wc.resize(cSize);
size_t cSize1;
mbstowcs_s(&cSize1, (wchar_t*)&wc[0], cSize, value.c_str(), cSize);
wc.pop_back();
return wc;
}
The question has several problems, but so do some of the answers. The idea of returning a pointer to allocated memory "and leaving it up to the caller to de-allocate" is asking for trouble. As a rule the best pattern is always to allocate and de-allocate within the same function. For example, something like:
wchar_t* buffer = new wchar_t[get_wcb_size(str)];
mbstowcs(buffer, str, get_wcb_size(str) + 1);
...
delete[] buffer;
In general, this requires two functions, one the caller calls to find out how much memory to allocate and a second to initialize or fill the allocated memory.
Unfortunately, the basic idea of using a function to return a "new" object is problematic -- not inherently, but because of the C++ inheritance of C memory handling. Using C++ and STL's strings/wstrings/strstreams is a better solution, but I felt the memory allocation thing needed to be better addressed.
Your problem has nothing to do with encodings, it's a simple matter of understanding basic C++. You are returning a pointer to a local variable from your function, which will have gone out of scope by the time anyone can use it, thus creating undefined behaviour (i.e. a programming error).
Follow this Golden Rule: "If you are using naked char pointers, you're Doing It Wrong. (Except for when you aren't.)"
I've previously posted some code to do the conversion and communicating the input and output in C++ std::string and std::wstring objects.
I did something like this. The first 2 zeros are because I don't know what kind of ascii type things this command wants from me. The general feeling I had was to create a temp char array. pass in the wide char array. boom. it works. The +1 ensures that the null terminating character is in the right place.
char tempFilePath[MAX_PATH] = "I want to convert this to wide chars";
int len = strlen(tempFilePath);
// Converts the path to wide characters
int needed = MultiByteToWideChar(0, 0, tempFilePath, len + 1, strDestPath, len + 1);
auto Ascii_To_Wstring = [](int code)->std::wstring
{
if (code>255 || code<0 )
{
throw std::runtime_error("Incorrect ASCII code");
}
std::string s{ char(code) };
std::wstring w{ s.begin(),s.end() };
return w;
};
I've tried implementing a function like this, but unfortunately it doesn't work:
const wchar_t *GetWC(const char *c)
{
const size_t cSize = strlen(c)+1;
wchar_t wc[cSize];
mbstowcs (wc, c, cSize);
return wc;
}
My main goal here is to be able to integrate normal char strings in a Unicode application. Any advice you guys can offer is greatly appreciated.
In your example, wc is a local variable which will be deallocated when the function call ends. This puts you into undefined behavior territory.
The simple fix is this:
const wchar_t *GetWC(const char *c)
{
const size_t cSize = strlen(c)+1;
wchar_t* wc = new wchar_t[cSize];
mbstowcs (wc, c, cSize);
return wc;
}
Note that the calling code will then have to deallocate this memory, otherwise you will have a memory leak.
Use a std::wstring instead of a C99 variable length array. The current standard guarantees a contiguous buffer for std::basic_string. E.g.,
std::wstring wc( cSize, L'#' );
mbstowcs( &wc[0], c, cSize );
C++ does not support C99 variable length arrays, and so if you compiled your code as pure C++, it would not even compile.
With that change your function return type should also be std::wstring.
Remember to set relevant locale in main.
E.g., setlocale( LC_ALL, "" ).
const char* text_char = "example of mbstowcs";
size_t length = strlen(text_char );
Example of usage "mbstowcs"
std::wstring text_wchar(length, L'#');
//#pragma warning (disable : 4996)
// Or add to the preprocessor: _CRT_SECURE_NO_WARNINGS
mbstowcs(&text_wchar[0], text_char , length);
Example of usage "mbstowcs_s"
Microsoft suggest to use "mbstowcs_s" instead of "mbstowcs".
Links:
Mbstowcs example
mbstowcs_s, _mbstowcs_s_l
wchar_t text_wchar[30];
mbstowcs_s(&length, text_wchar, text_char, length);
You're returning the address of a local variable allocated on the stack. When your function returns, the storage for all local variables (such as wc) is deallocated and is subject to being immediately overwritten by something else.
To fix this, you can pass the size of the buffer to GetWC, but then you've got pretty much the same interface as mbstowcs itself. Or, you could allocate a new buffer inside GetWC and return a pointer to that, leaving it up to the caller to deallocate the buffer.
I did something like this. The first 2 zeros are because I don't know what kind of ascii type things this command wants from me. The general feeling I had was to create a temp char array. pass in the wide char array. boom. it works. The +1 ensures that the null terminating character is in the right place.
char tempFilePath[MAX_PATH] = "I want to convert this to wide chars";
int len = strlen(tempFilePath);
// Converts the path to wide characters
int needed = MultiByteToWideChar(0, 0, tempFilePath, len + 1, strDestPath, len + 1);
Andrew Shepherd 's answer.
Andrew Shepherd 's answer is Good for me, I add up some fix :
1, remove the ending char L'\0', casue sometime it will trouble.
2, use mbstowcs_s
std::wstring wtos(std::string& value){
const size_t cSize = value.size() + 1;
std::wstring wc;
wc.resize(cSize);
size_t cSize1;
mbstowcs_s(&cSize1, (wchar_t*)&wc[0], cSize, value.c_str(), cSize);
wc.pop_back();
return wc;
}
The question has several problems, but so do some of the answers. The idea of returning a pointer to allocated memory "and leaving it up to the caller to de-allocate" is asking for trouble. As a rule the best pattern is always to allocate and de-allocate within the same function. For example, something like:
wchar_t* buffer = new wchar_t[get_wcb_size(str)];
mbstowcs(buffer, str, get_wcb_size(str) + 1);
...
delete[] buffer;
In general, this requires two functions, one the caller calls to find out how much memory to allocate and a second to initialize or fill the allocated memory.
Unfortunately, the basic idea of using a function to return a "new" object is problematic -- not inherently, but because of the C++ inheritance of C memory handling. Using C++ and STL's strings/wstrings/strstreams is a better solution, but I felt the memory allocation thing needed to be better addressed.
Your problem has nothing to do with encodings, it's a simple matter of understanding basic C++. You are returning a pointer to a local variable from your function, which will have gone out of scope by the time anyone can use it, thus creating undefined behaviour (i.e. a programming error).
Follow this Golden Rule: "If you are using naked char pointers, you're Doing It Wrong. (Except for when you aren't.)"
I've previously posted some code to do the conversion and communicating the input and output in C++ std::string and std::wstring objects.
auto Ascii_To_Wstring = [](int code)->std::wstring
{
if (code>255 || code<0 )
{
throw std::runtime_error("Incorrect ASCII code");
}
std::string s{ char(code) };
std::wstring w{ s.begin(),s.end() };
return w;
};
I want to copy a string into a char array, and not overrun the buffer.
So if I have a char array of size 5, then I want to copy a maximum of 5 bytes from a string into it.
what's the code to do that?
This is exactly what std::string's copy function does.
#include <string>
#include <iostream>
int main()
{
char test[5];
std::string str( "Hello, world" );
str.copy(test, 5);
std::cout.write(test, 5);
std::cout.put('\n');
return 0;
}
If you need null termination you should do something like this:
str.copy(test, 4);
test[4] = '\0';
First of all, strncpy is almost certainly not what you want. strncpy was designed for a fairly specific purpose. It's in the standard library almost exclusively because it already exists, not because it's generally useful.
Probably the simplest way to do what you want is with something like:
sprintf(buffer, "%.4s", your_string.c_str());
Unlike strncpy, this guarantees that the result will be NUL terminated, but does not fill in extra data in the target if the source is shorter than specified (though the latter isn't a major issue when the target length is 5).
Use function strlcpybroken link, and material not found on destination site if your implementation provides one (the function is not in the standard C library), yet it is rather widely accepted as a de-facto standard name for a "safe" limited-length copying function for zero-terminated strings.
If your implementation does not provide strlcpy function, implement one yourself. For example, something like this might work for you
char *my_strlcpy(char *dst, const char *src, size_t n)
{
assert(dst != NULL && src != NULL);
if (n > 0)
{
char *pd;
const char *ps;
for (--n, pd = dst, ps = src; n > 0 && *ps != '\0'; --n, ++pd, ++ps)
*pd = *ps;
*pd = '\0';
}
return dst;
}
(Actually, the de-facto accepted strlcpy returns size_t, so you might prefer to implement the accepted specification instead of what I did above).
Beware of the answers that recommend using strncpy for that purpose. strncpy is not a safe limited-length string copying function and is not supposed to be used for that purpose. While you can force strncpy to "work" for that purpose, it is still akin to driving woodscrews with a hammer.
Update: Thought I would try to tie together some of the answers, answers which have convinced me that my own original knee-jerk strncpy response was poor.
First, as AndreyT noted in the comments to this question, truncation methods (snprintf, strlcpy, and strncpy) are often not a good solution. Its often better to check the size of the string string.size() against the buffer length and return/throw an error or resize the buffer.
If truncation is OK in your situation, IMHO, strlcpy is the best solution, being the fastest/least overhead method that ensures null termination. Unfortunately, its not in many/all standard distributions and so is not portable. If you are doing a lot of these, it maybe worth providing your own implementation, AndreyT gave an example. It runs in O(result length). Also the reference specification returns the number of bytes copied, which can assist in detecting if the source was truncated.
Other good solutions are sprintf and snprintf. They are standard, and so are portable and provide a safe null terminated result. They have more overhead than strlcpy (parsing the format string specifier and variable argument list), but unless you are doing a lot of these you probably won't notice the difference. It also runs in O(result length). snprintf is always safe and that sprintf may overflow if you get the format specifier wrong (as other have noted, format string should be "%.<N>s" not "%<N>s"). These methods also return the number of bytes copied.
A special case solution is strncpy. It runs in O(buffer length), because if it reaches the end of the src it zeros out the remainder of the buffer. Only useful if you need to zero the tail of the buffer or are confident that destination and source string lengths are the same. Also note that it is not safe in that it doesn't necessarily null terminate the string. If the source is truncated, then null will not be appended, so call in sequence with a null assignment to ensure null termination: strncpy(buffer, str.c_str(), BUFFER_LAST); buffer[BUFFER_LAST] = '\0';
Some nice libc versions provide non-standard but great replacement for strcpy(3)/strncpy(3) - strlcpy(3).
If yours doesn't, the source code is freely available here from the OpenBSD repository.
void stringChange(string var){
char strArray[100];
strcpy(strArray, var.c_str());
}
I guess this should work. it'll copy form string to an char array.
i think snprintf() is much safe and simlest
snprintf ( buffer, 100, "The half of %d is %d", 60, 60/2 );
null character is append it end automatically :)
The most popular answer is fine but the null-termination is not generic. The generic way to null-terminate the char-buffer is:
std::string aString = "foo";
const size_t BUF_LEN = 5;
char buf[BUF_LEN];
size_t len = aString.copy(buf, BUF_LEN-1); // leave one char for the null-termination
buf[len] = '\0';
len is the number of chars copied so it's between 0 and BUF_LEN-1.
std::string my_string("something");
char* my_char_array = new char[5];
strncpy(my_char_array, my_string.c_str(), 4);
my_char_array[4] = '\0'; // my_char_array contains "some"
With strncpy, you can copy at most n characters from the source to the destination. However, note that if the source string is at most n chars long, the destination will not be null terminated; you must put the terminating null character into it yourself.
A char array with a length of 5 can contain at most a string of 4 characters, since the 5th must be the terminating null character. Hence in the above code, n = 4.
std::string str = "Your string";
char buffer[5];
strncpy(buffer, str.c_str(), sizeof(buffer));
buffer[sizeof(buffer)-1] = '\0';
The last line is required because strncpy isn't guaranteed to NUL terminate the string (there has been a discussion about the motivation yesterday).
If you used wide strings, instead of sizeof(buffer) you'd use sizeof(buffer)/sizeof(*buffer), or, even better, a macro like
#define ARRSIZE(arr) (sizeof(arr)/sizeof(*(arr)))
/* ... */
buffer[ARRSIZE(buffer)-1]='\0';
char mystring[101]; // a 100 character string plus terminator
char *any_input;
any_input = "Example";
iterate = 0;
while ( any_input[iterate] != '\0' && iterate < 100) {
mystring[iterate] = any_input[iterate];
iterate++;
}
mystring[iterate] = '\0';
This is the basic efficient design.
If you always have a buffer of size 5, then you could do:
std::string s = "Your string";
char buffer[5]={s[0],s[1],s[2],s[3],'\0'};
Edit:
Of course, assuming that your std::string is large enough.
I'm relatively new to C++ and I'm still getting to grips with the C++ Standard Library. To help transition from C, I want to format a std::string using printf-style formatters. I realise stringstream is a more type-safe approach, but I find myself finding printf-style much easier to read and deal with (at least, for the time being). This is my function:
using namespace std;
string formatStdString(const string &format, ...)
{
va_list va;
string output;
size_t needed;
size_t used;
va_start(va, format);
needed = vsnprintf(&output[0], 0, format.c_str(), va);
output.resize(needed + 1); // for null terminator??
va_end(va);
va_start(va, format);
used = vsnprintf(&output[0], output.capacity(), format.c_str(), va);
// assert(used == needed);
va_end(va);
return output;
}
This works, kinda. A few things that I am not sure about are:
Do I need to make room for a null terminator, or is this unnecessary?
Is capacity() the right function to call here? I keep thinking length() would return 0 since the first character in the string is a '\0'.
Occasionally while writing this string's contents to a socket (using its c_str() and length()), I have null bytes popping up on the receiving end, which is causing a bit of grief, but they seem to appear inconsistently. If I don't use this function at all, no null bytes appear.
With the current standard (the upcomming standard differs here) there is no guarantee that the internal memory buffer managed by the std::string will be contiguous, or that the .c_str() method returns a pointer to the internal data representation (the implementation is allowed to generate a contiguous read-only block for that operation and return a pointer into it. A pointer to the actual internal data can be retrieved with the .data() member method, but note that it also returns a constant pointer: i.e. it is not intended for you to modify the contents. The buffer return by .data() it is not necessarily null terminated, the implementation only needs to guarantee the null termination when c_str() is called, so even in implementations where .data() and .c_str() are called, the implementation can add the \0 to the end of the buffer when the latter is called.
The standard intended to allow rope implementations, so in principle it is unsafe to do what you are trying, and from the point of view of the standard you should use an intermediate std::vector (guaranteed contiguity, and there is a guarantee that &myvector[0] is a pointer to the first allocated block of the real buffer).
In all implementations I know of, the internal memory handled by std::string is actually a contiguous buffer and using .data() is undefined behavior (writting to a constant variable) but even if incorrect it might work (I would avoid it). You should use other libraries that are designed for this purpose, like boost::format.
About the null termination. If you finally decide to follow the path of the undefined... you would need to allocate extra space for the null terminator, since the library will write it into the buffer. Now, the problem is that unlike C-style strings, std::strings can hold null pointers internally, so you will have to resize the string down to fit the largest contiguous block of memory from the beginning that contains no \0. That is probably the issue you are finding with spurious null characters. This means that the bad approach of using vsnprintf(or the family) has to be followed by str.resize( strlen( str.c_str() ) ) to discard all contents of the string after the first \0.
Overall, I would advice against this approach, and insist in either getting used to the C++ way of formatting, using third party libraries (boost is third party, but it is also the most standard non-standard library), using vectors or managing memory like in C... but that last option should be avoided like the plague.
// A safe way in C++ of using vsnprintf:
std::vector<char> tmp( 1000 ); // expected maximum size
vsnprintf( &tmp[0], tmp.size(), "Hi %s", name.c_str() ); // assuming name to be a string
std::string salute( &tmp[0] );
Use boost::format, if you prefer printf() over streams.
Edit: Just to make this clear, actually I fully agree with Alan, who said you should use streams.
I think that there are no guarantees that the layout of the string as referenced by &output[0] is contiguous and that you can write to it.
Use std::vector instead as a buffer which is guaranteed to have contiguous storage since C++03.
using namespace std;
string formatStdString(const string &format, ...)
{
va_list va;
vector<string::value_type> output(1); // ensure some storage is allocated
size_t needed;
size_t used;
va_start(va, format);
needed = vsnprintf(&output[0], 0, format.c_str(), va);
output.resize(needed); // don't need null terminator
va_end(va);
// Here we should ensure that needed != 0
va_start(va, format);
used = vsnprintf(&output[0], output.size(), format.c_str(), va); // use size()
// assert(used == needed);
va_end(va);
return string(output.begin(), output.end());
}
NOTE: You'll have to set an initial size to the vector as the statement &output[0] can otherwise attempt to reference a non-existing item (as the internal buffer might not have been allocated yet).
1) You do not need to make space for the null terminator.
2) capacity() tells you how much space the string has reserved internally. length() tells you the length of the string. You probably don't want capacity()
The std::string class takes care of the null terminator for you.
However, as pointed out, since you're using vnsprintf to the raw underying string buffer (C anachronisms die hard...), you will have to ensure there is room for the null terminator.
My implementation for variable argument lists for functions is like this:
std::string format(const char *fmt, ...)
{
using std::string;
using std::vector;
string retStr("");
if (NULL != fmt)
{
va_list marker = NULL;
// initialize variable arguments
va_start(marker, fmt);
// Get formatted string length adding one for NULL
size_t len = _vscprintf(fmt, marker) + 1;
// Create a char vector to hold the formatted string.
vector<char> buffer(len, '\0');
int nWritten = _vsnprintf_s(&buffer[0], buffer.size(), len, fmt,
marker);
if (nWritten > 0)
{
retStr = &buffer[0];
}
// Reset variable arguments
va_end(marker);
}
return retStr;
}
To help transition from C, I want to
format a std::string using
printf-style formatters.
Just don't :(
If you do this, you're not actually learning C++ but coding C with a C++ compiler. It's a bad mindset, bad practice, and it propagates the problems that the std::o*stream classes were created to avoid.
I realise stringstream is a more
type-safe approach, but I find myself
finding printf-style much easier to
read and deal with (at least, for the
time being).
It's not a more typesafe approach. It is a typesafe approach. More than that, it minimizes dependencies, it lowers the number of issues you have to keep track of (like explicit buffer allocation and keeping track of the null char terminator) and it makes it easier to maintain your code.
Above that it is completely extensible / customizable:
you can extend locale formatting
you can define the i/o operations for custom data types
you can add new types of output formatting
you can add new buffer i/o types (making for example std::clog write to a window)
you can plug in different error handling policies.
std::o*stream family of classes is very powerful and once you learn to use it correctly there's little doubt you will not go back.
Unless you have very specific requirements your time will probably be much better spent learning the o*stream classes than writing printf in C++.