Conversion problem from char * to wchar_t* - c++

The below line works. It prints Success.
wchar_t * s1 = (wchar_t *) L"INSERT INTO OE(sqltext) VALUES('this text')";
if(WriteToSQL(s1) == 0)
printf( "Success"); //Success
else
printf( "Failed");
I need to take user input to create dynamic sql. I need to do what
L prefix is doing.
When i take input and do the required conversion, it does not work.
char input[100];
char sql[500];
printf("Enter input string :: ");
fgets(input,100,stdin);
for(int i=0;i<100;i++)
if(input[i]==10)
input[i]=0;
strcpy(sql,"INSERT INTO OE(sqltext) VALUES('");
strcat(sql,input);
strcat(sql,"')");
wchar_t wsql[500];
MultiByteToWideChar( CP_UTF8, 0, sql, strlen(sql),
wsql, strlen(sql) + 1 );
if(WriteToSQL(wsql) == 0)
printf( "Success");
else
printf( "Failed"); // It failed
Long conversassion but finally it did work. Hex memory dump and input from usta was most helpful. Thanks everybody for their time.

You can't just cast a char * to wchar_t * and expect it to work. You must do proper conversion, for example using MultiByteToWideChar function.
And in general, be very careful with type casts, and in particular avoid using C-style casts in C++ programs. This very case is a good example of why: you told the compiler to shut up ((SQLWCHAR *) sql), and in return got a problem at runtime. Use casts only when you are absolutely sure you are doing the right thing, and know better than the compiler. Not surprisingly, such cases are relatively rare...

Why not using wide chars the whole way?
Like this:
wchar_t input[100];
wchar_t sql[500];
wprintf(L"Enter input string :: ");
fgetws(input,100,stdin);
for(int i=0;i<100;i++)
if(input[i]==10)
input[i]=0;
wcscpy(sql,L"INSERT INTO OE(sqltext) VALUES('");
wcscat(sql,input);
wcscat(sql,L"')");
if(WriteToSQL(sql) == 0)
printf( "Success");
else
printf( "Failed"); // It failed
Warning:I did not test it, but it should work.

Manjoor, by using _tmain you opted using generic text so be consistent and try to stick to generic-text types throughout your program. That way your code will be cleaner and you won't need to use nasty string conversions like MultiByteToWideChar. If you are in position to change WriteToSQL function signature, pass argument 's' as SQLTCHAR* type. Declare sql and input variables as TCHAR arrays, use string routines from TCHAR.H (e.g. _tprintf instead printf, _T() macro for hardcoded strings...). For each routine you are using, go to its MSDN page and check Generic-text routine mappings to see which one you should use.
Google for Microsoft's support for UNICODE in order to better understand issue you had in the example you provided.

Related

Safe C++ std::string to TCHAR * conversion?

I am trying to convert a std::string to a TCHAR* for use in CreateFile(). The code i have compiles, and works, but Visual Studio 2013 comes up with a compiler warning:
warning C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
I understand why i get the warning, as in my code i use std::copy, but I don't want to define D_SCL_SECURE_NO_WARNINGS if at all possible, as they have a point: std::copy is unsafe/unsecure. As a result, I'd like to find a way that doesn't throw this warning.
The code that produces the warning:
std::string filename = fileList->getFullPath(index);
TCHAR *t_filename = new TCHAR[filename.size() + 1];
t_filename[filename.size()] = 0;
std::copy(filename.begin(), filename.end(), t_filename);
audioreader.setFile(t_filename);
audioreader.setfile() calls CreateFile() internally, which is why i need to convert the string.
fileList and audioreader are instances of classes i wrote myself, but I'd rather not change the core implementation of either if at all possible, as it would mean I'd need to change a lot of implementation in other areas of my program, where this conversion only happens in that piece of code. The method I used to convert there was found in a solution i found at http://www.cplusplus.com/forum/general/12245/#msg58523
I've seen something similar in another question (Converting string to tchar in VC++) but i can't quite fathom how to adapt the answer to work with mine as the size of the string isn't constant. All other ways I've seen involve a straight (TCHAR *) cast (or something equally unsafe), which as far as i know about the way TCHAR and other windows string types are defined, is relatively risky as TCHAR could be single byte or multibyte characters depending on UNICODE definition.
Does anyone know a safe, reliable way to convert a std::string to a TCHAR* for use in functions like CreateFile()?
EDIT to address questions in the comments and answers:
Regarding UNICODE being defined or not: The project in VS2013 is a win32 console application, with #undef UNICODE at the top of the .cpp file containing main() - what is the difference between UNICODE and _UNICODE? as i assume the underscore in what Amadeus was asking is significant.
Not directly related to the question but may add perspective: This program is not going to be used outside the UK, so ANSI vs UNICODE does not matter for this. This is part of a personal project to create an audio server and client. As a result you may see some bits referencing network communication. The aim of this program is to get me using Xaudio and winsock. The conversion issue purely deals with the loading of the file on the server-side so it can open it and start reading chunks to transmit. I'm testing with .wav files found in c:/windows/media
Filename encoding: I read the filenames in at runtime by using FindFirstFileA() and FindNextFileA(). The names are retrieved by looking at cFilename in a WIN32_FIND_DATAA structure. They are stored in a vector<string> (wrapped in a unique_ptr if that matters) but that could be changed. I assume this is what Dan Korn means.
More info about the my classes and functions:
The following are spread between AudioReader.h, Audioreader.cpp, FileList.h, FileList.cpp and ClientSession.h. The fragment above is in ClientSession.cpp. Note that in most of my files i declare using namespace std;
shared_ptr<FileList> fileList; //ClientSession.h
AudioReader audioreader; //ClientSession.h
string _storedpath; //FileList.h
unique_ptr<vector<string>> _filenames; //FileList.h
//FileList.cpp
string FileList::getFullPath(int i)
{
string ret = "";
unique_lock<mutex> listLock(listmtx);
if (static_cast<size_t>(i) < _count)
{
ret = _storedpath + _filenames->at(i);
}
else
{
//rather than go out of bounds, return the last element, as returning an error over the network is difficult at present
ret = _storedpath + _filenames->at(_count - 1);
}
return ret;
}
unique_ptr<AudioReader_Impl> audioReaderImpl; //AudioReader.h
//AudioReader.cpp
HRESULT AudioReader::setFile(TCHAR * fileName)
{
return audioReaderImpl->setFile(fileName);
}
HANDLE AudioReader_Impl::fileHandle; //AudioReader.cpp
//AudioReader.cpp
HRESULT AudioReader_Impl::setFile(TCHAR * fileName)
{
fileHandle = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (SetFilePointer(fileHandle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
If you do not need to support the string containing UTF-8 (or another multi-byte encoding) then simply use the ANSI version of Windows API:
handle = CreateFileA( filename.c_str(), .......)
You might need to rejig your code for this as you have the CreateFile buried in a function that expects TCHAR. That's not advised these days; it's a pain to splatter T versions of everything all over your code and it has flow-on effects (such as std::tstring that someone suggested - ugh!)
There hasn't been any need to support dual compilation from the same source code since about 1998. Windows API has to support both versions for backward compatibility but your own code does not have to.
If you do want to support the string containing UTF-8 (and this is a better idea than using UTF-16 everywhere) then you will need to convert it to a UTF-16 string in order to call the Windows API.
The usual way to do this is via the Windows API function MultiByteToWideChar which is a bit awkward to use correctly, but you could wrap it up in a function:
std::wstring make_wstring( std::string const &s );
that invokes MultiByteToWideChar to return a UTF-16 string that you can then pass to WinAPI functions by using its .c_str() function.
See this codereview thread for a possible implementation of such a function (although note discussion in the answers)
The root of your problem is that you are mixing TCHARs and non-TCHARs. Even if you get it to work on your machine, unless you do it precisely right, it will fail when non-ASCII characters are used in the path.
If you can use std::tstring instead of regular string, then you won't have to worry about format conversions or codepage versus Unicode issues.
If not, you can use conversion functions like MultiByteToWideChar but make sure you understand the encoding used in the source string or it will just make things worse.
Try this instead:
std::string filename = fileList->getFullPath(index);
#ifndef UNICODE
audioreader.setFile(filename.c_str());
#else
std::wstring w_filename;
int len = MultiByteToWideChar(CP_ACP, 0, filename.c_str(), filename.length(), NULL, 0);
if (len > 0)
{
w_filename.resize(len);
MultiByteToWideChar(CP_ACP, 0, filename.c_str(), filename.length(), &w_filename[0], len);
}
audioreader.setFile(w_filename.c_str());
#endif
Alternatively:
std::string filename = fileList->getFullPath(index);
#ifndef UNICODE
audioreader.setFile(filename.c_str());
#else
std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>> conv;
std::wstring w_filename = conv.from_bytes(filename);
audioreader.setFile(w_filename.c_str());
#endif

Is there a file pointer (FILE*) that points to nothing?

For some reasons I need a file pointer (FILE*) that points to nothing. It means I can pass it to fprintf function and fprintf ignore the file pointer.
for example:
void my_function()
{
FILE* nothing = NULL; // NULL is not correct.
int n = fprintf(nothing, "PI = %f", 3.14); // Note: When nothing = NULL, error ocured.
}
Is there a file pointer (FILE*) that points to nothing?
To solve your actual problem (stated in comments), use snprintf instead of printf, provided that it's available in your C++ implementation (which is not guaranteed in C++03). Pass in a null pointer for the buffer and 0 for the size. Nothing is written, but the return value is the length of the formatted string (excluding nul terminator).
[Edit: oops, I forgot that snprintf on Windows doesn't conform to C99. It returns an error if truncation occurs, not the required length. I don't know what they're going to do about the fact that C++0x requires C99-conforming snprintf.]
To answer your question, you can fopen /dev/null on UNIX-like systems or nul on Windows. Writes to the resulting FILE* have no effect. However, there is no portable null-device.
No.
Your code will cause a run-time exception. You can use /dev/null for example, if you're running on an OS that supports it, but there's nothing like that built-in in C++.
why not wrapping the fprintf method call with an if (NULL != nothing) statement?
Although as NirMH said, you can enclose it in if (nothing != NULL), there is another way. You can open a file in read mode (with "r") and when you send it to fprintf, the write is ignored (Edit: as discussed in the comments, remember to set n=0 if it was negative as returned by fprintf).
I personally suggest the first method though. The only reason I might do the second is if I can't change the function.
If you don't care if your code is system dependent, you can use /dev/null in linux, nul in windows etc,
You are making a design mistake.
Obviously, what you want to know is the number of chars needed to write your number.
You are using _*printf_ for that, which is a good idea. You just want to compute the number of chars "written", hence needed. But you don't want anything to be displayed, so you pricked fprintf instead of just printf. But fprintf doesn't work without a FILE to write in...
Like Steve said, you should rather use snprintf(), which writes into a string in memory.
As steve says, snprintf provided with a NULL string should work as intended except on windows. Then on windows, just provide it with an temporary string which you'll discard afterward.
size_t computeNumCharsNeededToPrintMyStuff(double d)
{
size_t ret = 0;
size_t maxBuffSize = 100; // should be plenty enough
char *tmpBuff = new char[maxBuffSize ];
ret = snprintf(tmpBuff, maxBuffSize, "PI = %f", d);
delete[] tmpBuff;
return ret;
}
Then you just call :
n = computeNumCharsNeededToPrintMyStuff(3.14)
(This func could be enhanced to compute the size needed to display anything but I'd rather keep it simple for now.)
Do you mean something like this?
#include <stdio.h>
int main()
{
FILE *fdevnull = fopen("/dev/null", "w");
printf("printf length=%d\n", fprintf(fdevnull, "Hello World '%s'=%f\n", "PI", 3.14159));
fclose(fdevnull);
return 0;
}

Concatenate Strings in C/C++

How do I concatenate Strings with C/C++?
I tried the following ways:
PS: errorInfo is a char * I should return it.
errorInfo = strcat("Workflow: ", strcat(
workflowToString(workflow).utf8(), strcat(" ERROR: ",
errorCode.utf8)));
sprintf(errorInfo, "Workflow %s ERROR: %s",
workflowToString(workflow).utf8(), errorCode.utf8());
errorInfo = "Workflow: " + workflowToString(workflow).utf8() + " ERROR: " + errorCode.utf8;
Just the sprintf compiles but when running my application crash.
PS: I'm using NDK from Android
There ISN'T such a language as C/C++. There is C, and there is C++.
In C++ you concatenate std::string's by using operator+
In C, you use strcat
I know this doesn't quite answer your question, this is just an outcry :)
According to this page strcat does the following:
Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a new null-character is appended at the end of the new string formed by the concatenation of both in destination.
In your implementation, however, "Workflow: " is a constant string. You cannot modify that string, which is what strcat would do. In order to do that, create a string like:
char message[1000];
strcpy(message, "Workflow: ");
strcat(message, "other string");
....
However, be careful about the utf8 character encoding because one utf8 code point could be multiple chars long.
Concatenation is almost always the wrong idiom for string building, especially in C. It's error-prone, clutters your code, and has extremely bad asymptotic performance (i.e. O(n^2) instead of O(n) for building a string of length n).
Instead you should use the snprintf function, as in:
snprintf(buf, sizeof buf, "Workflow: %s ERROR: %s", workflow, error);
or if you're writing to a file/socket/etc. and don't need to keep the resulting string in memory, simply use fprintf to begin with.
With string literals you can simple use:
char str[] = "foo" " bar";
const char *s = " 1 " " 2 ";
s = " 3 " " 4 ";
By using strcat(), you are working in c, not c++.
c is not going to automatically manage memory for you.
c can be confusing since sometimes it seems like it has a string data type when all it is doing is providing you a string interface to arrays of characters.
For one thing, the first argument to strcat() has to be writable and have enough room to add the second string.
char *out = strcat("This", "nThat");
is asking c to stomp on string literal memory.
In general, you should NEVER use strcat()/sprintf, as in the above "chosen" answer. You can overwrite memory that way. Use strncat()/snprintf() instead to avoid buffer overruns. If you don't know the size to pass to "n" in strncat(), you're likely doing something wrong.
One way to do this in c would be:
#define ERROR_BUF_SIZE 2048 // or something big enough, you have to know in c
char errorInfo[ERROR_BUF_SIZE];
snprintf(errorInfo, ERROR_BUF_SIZE, "Workflow %s ERROR: %s",
workflowToString(workflow).utf8(), errorCode.utf8());
or similarly using strncpy/strncat
There are many ways you can concatenate in C while using Android NDK:
Two ways I used are:
strcat
sprintf
here is example:
enter code here
strcat
char* buffer1=(char*)malloc(250000);
char* buffer2=(char*)malloc(250000);
char* buffer3=(char*)malloc(250000);
buffer1 = strcat(buffer1, buffer2);
sprintf
sprintf(buffer3,"this is buffer1: %s and this is buffer2:%s",buffer1,buffer2);`
sprintf returns length of your string
strcat is not recommended as its use more memory..
you can use sprintf or others like strcpy.
Hope it helps.

understanding the dangers of sprintf(...)

OWASP says:
"C library functions such as strcpy
(), strcat (), sprintf () and vsprintf
() operate on null terminated strings
and perform no bounds checking."
sprintf writes formatted data to string
int sprintf ( char * str, const char * format, ... );
Example:
sprintf(str, "%s", message); // assume declaration and
// initialization of variables
If I understand OWASP's comment, then the dangers of using sprintf are that
1) if message's length > str's length, there's a buffer overflow
and
2) if message does not null-terminate with \0, then message could get copied into str beyond the memory address of message, causing a buffer overflow
Please confirm/deny. Thanks
You're correct on both problems, though they're really both the same problem (which is accessing data beyond the boundaries of an array).
A solution to your first problem is to instead use std::snprintf, which accepts a buffer size as an argument.
A solution to your second problem is to give a maximum length argument to snprintf. For example:
char buffer[128];
std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");
// std::strcmp(buffer, "This is a test\n") == 0
If you want to store the entire string (e.g. in the case sizeof(buffer) is too small), run snprintf twice:
int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");
++length; // +1 for null terminator
char *buffer = new char[length];
std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");
(You can probably fit this into a function using va or variadic templates.)
Both of your assertions are correct.
There's an additional problem not mentioned. There is no type checking on the parameters. If you mismatch the format string and the parameters, undefined and undesirable behavior could result. For example:
char buf[1024] = {0};
float f = 42.0f;
sprintf(buf, "%s", f); // `f` isn't a string. the sun may explode here
This can be particularly nasty to debug.
All of the above lead many C++ developers to the conclusion that you should never use sprintf and its brethren. Indeed, there are facilities you can use to avoid all of the above problems. One, streams, is built right in to the language:
#include <sstream>
#include <string>
// ...
float f = 42.0f;
stringstream ss;
ss << f;
string s = ss.str();
...and another popular choice for those who, like me, still prefer to use sprintf comes from the boost Format libraries:
#include <string>
#include <boost\format.hpp>
// ...
float f = 42.0f;
string s = (boost::format("%1%") %f).str();
Should you adopt the "never use sprintf" mantra? Decide for yourself. There's usually a best tool for the job and depending on what you're doing, sprintf just might be it.
Yes, it is mostly a matter of buffer overflows. However, those are quite serious business nowdays, since buffer overflows are the prime attack vector used by system crackers to circumvent software or system security. If you expose something like this to user input, there's a very good chance you are handing the keys to your program (or even your computer itself) to the crackers.
From OWASP's perspective, let's pretend we are writing a web server, and we use sprintf to parse the input that a browser passes us.
Now let's suppose someone malicious out there passes our web browser a string far larger than will fit in the buffer we chose. His extra data will instead overwrite nearby data. If he makes it large enough, some of his data will get copied over the webserver's instructions rather than its data. Now he can get our webserver to execute his code.
Your 2 numbered conclusions are correct, but incomplete.
There is an additional risk:
char* format = 0;
char buf[128];
sprintf(buf, format, "hello");
Here, format is not NULL-terminated. sprintf() doesn't check that either.
Your interpretation seems to be correct. However, your case #2 isn't really a buffer overflow. It's more of a memory access violation. That's just terminology though, it's still a major problem.
The sprintf function, when used with certain format specifiers, poses two types of security risk: (1) writing memory it shouldn't; (2) reading memory it shouldn't. If snprintf is used with a size parameter that matches the buffer, it won't write anything it shouldn't. Depending upon the parameters, it may still read stuff it shouldn't. Depending upon the operating environment and what else a program is doing, the danger from improper reads may or may not be less severe than that from improper writes.
It is very important to remember that sprintf() adds the ASCII 0 character as string terminator at the end of each string. Therefore, the destination buffer must have at least n+1 bytes (To print the word "HELLO", a 6-byte buffer is required, NOT 5)
In the example below, it may not be obvious, but in the 2-byte destination buffer, the second byte will be overwritten by ASCII 0 character. If only 1 byte was allocated for the buffer, this would cause buffer overrun.
char buf[3] = {'1', '2'};
int n = sprintf(buf, "A");
Also note that the return value of sprintf() does NOT include the null-terminating character. In the example above, 2 bytes were written, but the function returns '1'.
In the example below, the first byte of class member variable 'i' would be partially overwritten by sprintf() (on a 32-bit system).
struct S
{
char buf[4];
int i;
};
int main()
{
struct S s = { };
s.i = 12345;
int num = sprintf(s.buf, "ABCD");
// The value of s.i is NOT 12345 anymore !
return 0;
}
I pretty much have stated a small example how you could get rid of the buffer size declaration for the sprintf (if you intended to, of course!) and no snprintf envolved ....
Note: This is an APPEND/CONCATENATION example, take a look at here

C++: how to get fprintf results as a std::string w/o sprintf

I am working with an open-source UNIX tool that is implemented in C++, and I need to change some code to get it to do what I want. I would like to make the smallest possible change in hopes of getting my patch accepted upstream. Solutions that are implementable in standard C++ and do not create more external dependencies are preferred.
Here is my problem. I have a C++ class -- let's call it "A" -- that currently uses fprintf() to print its heavily formatted data structures to a file pointer. In its print function, it also recursively calls the identically defined print functions of several member classes ("B" is an example). There is another class C that has a member std::string "foo" that needs to be set to the print() results of an instance of A. Think of it as a to_str() member function for A.
In pseudocode:
class A {
public:
...
void print(FILE* f);
B b;
...
};
...
void A::print(FILE *f)
{
std::string s = "stuff";
fprintf(f, "some %s", s);
b.print(f);
}
class C {
...
std::string foo;
bool set_foo(std::str);
...
}
...
A a = new A();
C c = new C();
...
// wish i knew how to write A's to_str()
c.set_foo(a.to_str());
I should mention that C is fairly stable, but A and B (and the rest of A's dependents) are in a state of flux, so the less code changes necessary the better. The current print(FILE* F) interface also needs to be preserved. I have considered several approaches to implementing A::to_str(), each with advantages and disadvantages:
Change the calls to fprintf() to sprintf()
I wouldn't have to rewrite any format strings
print() could be reimplemented as: fprint(f, this.to_str());
But I would need to manually allocate char[]s, merge a lot of c strings , and finally convert the character array to a std::string
Try to catch the results of a.print() in a string stream
I would have to convert all of the format strings to << output format. There are hundreds of fprintf()s to convert :-{
print() would have to be rewritten because there is no standard way that I know of to create an output stream from a UNIX file handle (though this guy says it may be possible).
Use Boost's string format library
More external dependencies. Yuck.
Format's syntax is different enough from printf() to be annoying:
printf(format_str, args) -> cout << boost::format(format_str) % arg1 % arg2 % etc
Use Qt's QString::asprintf()
A different external dependency.
So, have I exhausted all possible options? If so, which do you think is my best bet? If not, what have I overlooked?
Thanks.
Here's the idiom I like for making functionality identical to 'sprintf', but returning a std::string, and immune to buffer overflow problems. This code is part of an open source project that I'm writing (BSD license), so everybody feel free to use this as you wish.
#include <string>
#include <cstdarg>
#include <vector>
#include <string>
std::string
format (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
std::string buf = vformat (fmt, ap);
va_end (ap);
return buf;
}
std::string
vformat (const char *fmt, va_list ap)
{
// Allocate a buffer on the stack that's big enough for us almost
// all the time.
size_t size = 1024;
char buf[size];
// Try to vsnprintf into our buffer.
va_list apcopy;
va_copy (apcopy, ap);
int needed = vsnprintf (&buf[0], size, fmt, ap);
// NB. On Windows, vsnprintf returns -1 if the string didn't fit the
// buffer. On Linux & OSX, it returns the length it would have needed.
if (needed <= size && needed >= 0) {
// It fit fine the first time, we're done.
return std::string (&buf[0]);
} else {
// vsnprintf reported that it wanted to write more characters
// than we allotted. So do a malloc of the right size and try again.
// This doesn't happen very often if we chose our initial size
// well.
std::vector <char> buf;
size = needed;
buf.resize (size);
needed = vsnprintf (&buf[0], size, fmt, apcopy);
return std::string (&buf[0]);
}
}
EDIT: when I wrote this code, I had no idea that this required C99 conformance and that Windows (as well as older glibc) had different vsnprintf behavior, in which it returns -1 for failure, rather than a definitive measure of how much space is needed. Here is my revised code, could everybody look it over and if you think it's ok, I will edit again to make that the only cost listed:
std::string
Strutil::vformat (const char *fmt, va_list ap)
{
// Allocate a buffer on the stack that's big enough for us almost
// all the time. Be prepared to allocate dynamically if it doesn't fit.
size_t size = 1024;
char stackbuf[1024];
std::vector<char> dynamicbuf;
char *buf = &stackbuf[0];
va_list ap_copy;
while (1) {
// Try to vsnprintf into our buffer.
va_copy(ap_copy, ap);
int needed = vsnprintf (buf, size, fmt, ap);
va_end(ap_copy);
// NB. C99 (which modern Linux and OS X follow) says vsnprintf
// failure returns the length it would have needed. But older
// glibc and current Windows return -1 for failure, i.e., not
// telling us how much was needed.
if (needed <= (int)size && needed >= 0) {
// It fit fine so we're done.
return std::string (buf, (size_t) needed);
}
// vsnprintf reported that it wanted to write more characters
// than we allotted. So try again using a dynamic buffer. This
// doesn't happen very often if we chose our initial size well.
size = (needed > 0) ? (needed+1) : (size*2);
dynamicbuf.resize (size);
buf = &dynamicbuf[0];
}
}
I am using #3: the boost string format library - but I have to admit that I've never had any problem with the differences in format specifications.
Works like a charm for me - and the external dependencies could be worse (a very stable library)
Edited: adding an example how to use boost::format instead of printf:
sprintf(buffer, "This is a string with some %s and %d numbers", "strings", 42);
would be something like this with the boost::format library:
string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42);
Hope this helps clarify the usage of boost::format
I've used boost::format as a sprintf / printf replacement in 4 or 5 applications (writing formatted strings to files, or custom output to logfiles) and never had problems with format differences. There may be some (more or less obscure) format specifiers which are differently - but I never had a problem.
In contrast I had some format specifications I couldn't really do with streams (as much as I remember)
You can use std::string and iostreams with formatting, such as the setw() call and others in iomanip
The {fmt} library provides fmt::sprintf function that performs printf-compatible formatting (including positional arguments according to POSIX specification) and returns the result as std::string:
std::string s = fmt::sprintf("The answer is %d.", 42);
Disclaimer: I'm the author of this library.
The following might be an alternative solution:
void A::printto(ostream outputstream) {
char buffer[100];
string s = "stuff";
sprintf(buffer, "some %s", s);
outputstream << buffer << endl;
b.printto(outputstream);
}
(B::printto similar), and define
void A::print(FILE *f) {
printto(ofstream(f));
}
string A::to_str() {
ostringstream os;
printto(os);
return os.str();
}
Of course, you should really use snprintf instead of sprintf to avoid buffer overflows. You could also selectively change the more risky sprintfs to << format, to be safer and yet change as little as possible.
You should try the Loki library's SafeFormat header file (http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf). It's similar to boost's string format library, but keeps the syntax of the printf(...) functions.
I hope this helps!
Is this about serialization? Or printing proper?
If the former, consider boost::serialization as well. It's all about "recursive" serialization of objects and sub-object.
Very very late to the party, but here's how I'd attack this problem.
1: Use pipe(2) to open a pipe.
2: Use fdopen(3) to convert the write fd from the pipe to a FILE *.
3: Hand that FILE * to A::print().
4: Use read(2) to pull bufferloads of data, e.g. 1K or more at a time from the read fd.
5: Append each bufferload of data to the target std::string
6: Repeat steps 4 and 5 as needed to complete the task.