is it possible to use vsnprintf to start printing from the exact value in the array?
For example, I would like to use vsnprintf to print from 25th character in the array.
Can I just this code?
va_list args;
#define length 100
char debug[length];
va_start(args, fmt);
vsnprintf(debug[25], length, fmt, args);
a_debug(devh,debug);
va_end(args);
Print from 25th character? You mean print into the buffer starting at the 25th byte position? Try this:
vsnprintf(debug + 25, length - 25, fmt, args);
Related
I have an old function which I can't change the API
void TraceMsg(const char* fmt, ...)
{
if (!m_MessageFunctions[TraceLevel]) return;
char msgBuffer[MAX_LOG_MSG];
va_list argList;
va_start(argList, fmt);
vsnprintf(msgBuffer, MAX_LOG_MSG, fmt, argList);
va_end(argList);
m_MessageFunctions[TraceLevel](msgBuffer);
}
MAX_LOG_MSG = 2048
I got into a phase where I would like to allocate more space for the messages for the logger in a dynamic way
I have read this article: https://code-examples.net/en/q/4904e5
and changed my code into:
void TraceMsg(const char* fmt, ...)
{
if (!m_MessageFunctions[TraceLevel]) return;
va_list argList;
va_start(argList, fmt);
size_t size = vsnprintf(NULL, 0,fmt, argList);
char* msgBuffer = new char[size];
vsnprintf(msgBuffer, size, fmt, argList);
va_end(argList);
m_MessageFunctions[TraceLevel](msgBuffer);
delete[] msgBuffer;
}
how ever I get wierd characters like
2022-05-03 12:13:20,939 INFO Make graph edge Bayer#LSC_1_2 ->Input#DeMux_LSC§§§§н
2022-05-03 12:13:20,939 INFO Make graph edge Bayer#RGB_IR_2_0 ->0#Mux_X2B_BP§§§§нннннњйн‚€нннннннннннннннннннннннннннннннннннн
Can you please help?
The return value of vsnprintf is
The number of characters that would have been written if n had been
sufficiently large, not counting the terminating null character.
So you need to add 1 to this to make room for the null terminator.
I've asked the same question about strncpy, but there the string ends up containing the whole input string. When passing a string to vsnprintf the last character always gets chopped off: https://rextester.com/UIQMX91570
For simplicity I've also included the live example link above inline in the code:
void bar(const char* format, va_list vlist) {
const auto buf_size = vsnprintf(nullptr, 0U, format, vlist);
string buffer(buf_size, '\0');
vsnprintf(data(buffer), buf_size, format, vlist);
cout << data(buffer) << endl;
}
void foo(const char* format, ...) {
va_list vlist;
va_start(vlist, format);
bar(format, vlist);
va_end(vlist);
}
If I call this with: foo("lorem ipsum %d", 13) the output I get is:
lorem ipsum 1
Where as I would have expected: lorem ipsum 13
Can anyone explain the discrepancy? When I debug I get a buf_size of 14 which should be enough to contain the entire string, yet it does not :(
Because man page clearly says that
If the output was truncated due to this limit then the return value is the number of characters (not including the trailing '\0') which would have been written to the final string if enough space had been available.
If you'd check the return value of your second vsnprintf call, you'd see that return value is equal to the size, as in the man page:
Thus, a return value of size or more means that the output was
truncated.
Can anyone explain the discrepancy?
Because their documented behavior is different.
strncpy()
If count is reached before the entire array src was copied, the resulting character array is not null-terminated.
but vsnprintf()
At most buf_size-1 characters are written. The resulting character string will be terminated with a null character, unless buf_size is zero.
emphasis is mine.
The buf_size parameter to vsnprintf specifies how many characters to write, including the terminating NUL character. The return value is the number of characters produced, not including the terminating NUL character.
You want
const auto buf_size = vsnprintf(nullptr, 0U, format, vlist) + 1;
I have the following functions:
void raiseError(const char *msg, ...)
{
va_list ap;
va_start(ap, msg); // use variable arg list
int size = vsnprintf(nullptr, 0, msg, ap);
std::vector<char> s(size+1, 0);
vsnprintf(s.data(), size, msg, ap);
va_end(ap);
errorString = std::string(s.data()));
}
When I call
raiseError("File not found in <%s> : <%s>", "a", "b" );
The first vsnprintf call (which computes the final string size) returns the correct value of 27. But the final string is:
"File not found in <**A*> :"
Where the '*' characters are random across program launches.
Also, the program works in MinGW, the issue is only seen with linux gcc.
What is wrong in my code?
You can't use ap more than once. Look into using va_copy.
I have a logging function which works like this:
// public function:
void ConsoleUI::log(const std::string& format, ...) {
va_list args;
va_start(args, format);
log(format, args);
va_end(args);
}
// overloaded private function:
void ConsoleUI::log(const std::string& format, va_list args) {
//wprintw( outWin, format.c_str(), args);
//wprintw( outWin, "\n");
printf( format.c_str(), args);
printf( "\n");
}
(Side note: This should do the exact same thing as just a normal printf, so it's pretty useless in this state. That's because this is a minumal working example. In the end, this should work with ncurses - see commented section.)
I then create an instance of the ConsoleUI class, called ui:
ConsoleUI ui;
And later I use it to log stuff, more precisely, a time_duration in microseconds:
now = boost::posix_time::microsec_clock::local_time();
boost::posix_time::time_duration delta = now - lastTime;
double dt = 1e-6*(double)delta.total_microseconds();
lastTime = now;
ui.log( "logged dt: %f", dt );
ui.log( "logged dt 2: %lu", delta.total_microseconds());
printf( "dt: %f\n", dt);
printf( "dt 2: %lu\n", delta.total_microseconds());
The output I get:
logged dt: 0.000000
logged dt 2: 140736013247624
dt: 0.018739
dt 2: 18739
What I expected:
logged dt: 0.018739
logged dt 2: 18739
dt: 0.018739
dt 2: 18739
Note that over multiple calls of this, the values in the last two lines change slightly (as is to be expected of a delta time) and the first two values don't change - which looks like there's something wrong with the format.
So, bottom line: Calling printf directly works, but passing it down the logger and then calling printf doesn't work...
Since you can't actually pass your arguments from log to printf, you are passing a va_list - this is the right solution, but you can't pass a va_list to printf.
This line:
printf( format.c_str(), args);
prints args with the format in format. Given that args is of the type va_list (typically implemented in the comiler ABI as a void pointer) it will not represent the data that you expect in the format string. If you use
vprintf( format.c_str(), args);
it should work just fine, that's what vprintf is for.
(Note that it's nothing to do with "loss of precision" - it's passing incorrect type of argument to printf - and since it's a programmaticly formulated string, even enabling warnings is not going to work).
Sorry, I know this is really basic, but I don't know how to search it the proper way, so here we go.
I'm trying to call MessageBoxA, and I want the message to replace '%s' with something else. Example:
MessageBoxA(0, TEXT("You have %s items"), "title", 0);
Can anyone help me? And once again, I know this is really basic, sorry.
You have to build the string yourself. In C++, this is typically done with std::ostringstream, e.g.:
#include <sstream>
...
std::ostringstream message;
message << "You have " << numItems << " items";
MessageBoxA(NULL, message.str().c_str(), "title", MB_OK);
In C, this is typically done with snprintf(3):
#include <stdio.h>
...
char buffer[256]; // Make sure this is big enough
snprintf(buffer, sizeof(buffer), "You have %d items", numItems);
MessageBoxA(NULL, buffer, "title", MB_OK);
You can write a utility function to build a std::string from a printf-style format:
#include <cstdio>
#include <cstdarg>
#include <string>
#include <vector>
std::string build_string(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
size_t len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, fmt);
vsnprintf(vec.data(), len + 1, fmt, args);
va_end(args);
return std::string(vec.begin(), vec.end() - 1);
}
With this function in place, you can create arbitrary strings and pass pointers to their contents as downward-arguments:
MessageBoxA(0, build_string("You have %d items", item_count).c_str(), "title", 0);
It has the advantage of simplicity (several lines of code using only stdio without dependency on iostreams), and has no arbitrary limits on the size of string.
For MessageBoxA it is:
char szBuf[120];
snprintf(szBuf,120, "You have %s items",nItemCount);
MessageBoxA(0, szBuf, "title", 0);
It is ugly, but it will fit your need.
Use boost::format.
In your example: MessageBoxA(0, (boost::format("You have %1 items") % "title").c_str(), 0);
One advantage is that you don't need to remember all those %s codes anymore, another is that you're no longer limited by the set of built-in flags.
The ( ).c_str() is needed because MessageBoxA is a C interface, not C++, and c_str() converts a C++ string to a C string.