I use the following methods to write to a trace file (inspired by https://stackoverflow.com/a/16046064/283561)
void Tracing::Info( const char* content, ...)
{
va_list paramList;
va_start( paramList, content );
Tracing::AddRecord(boost::log::trivial::info, content, paramList);
va_end( paramList );
}
void Tracing::AddRecord(boost::log::trivial::severity_level sev, const char* content, va_list paramList)
{
int size = vsnprintf(0, 0, content, paramList) + 1;
if (size > 0)
{
boost::scoped_array<char> formattedString(new char[size]);
vsnprintf(formattedString.get(), size, content, paramList);
boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> & lg = my_logger::get();
BOOST_LOG_SEV(lg, sev) << formattedString.get();
}
}
If I call the method the following way under Linux (CentOS 7, GCC 4.8.2):
Tracing trace;
trace.Error("No %s root tag found!", rootTag.c_str());
it segfaults at the second call of vsnprintf in AddRecord().
If it's called with a numeric formatter (e.g. %i), it works fine. I've used these methods for years under Windows (VS2008/2010) with no problems.
Am I missing something obvious here?
You can't reuse the va_list like that; you have to use the va_copy() routine to make a new va_list entity, and use that on the second vsprintf; something like:
void Tracing::AddRecord(boost::log::trivial::severity_level sev, const char* content, va_list paramList)
{
va_list parm_copy;
va_copy(parm_copy, paramList);
int size = vsnprintf(0, 0, content, paramList) + 1;
if (size > 0)
{
boost::scoped_array<char> formattedString(new char[size]);
vsnprintf(formattedString.get(), size, content, parm_copy);
boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> & lg = my_logger::get();
BOOST_LOG_SEV(lg, sev) << formattedString.get();
}
va_end(parm_copy);
}
The problem is that the operation va_arg, which is used in the first vsnprintf alters the state of the va_list, making it invalid for use in the second vsnprintf as-is.
The problem can be easily seen by using a small C program.
#include <stdio.h>
#include <stdarg.h>
void
do_log(const char *item, va_list items)
{
#ifndef EVIL
va_list itemcopy;
va_copy(itemcopy, items);
#else
#define itemcopy items
#endif
int len = vsnprintf(0, 0, item, items);
if (len > 0) {
char buffer[2048];
vsnprintf(buffer, 2047, item, itemcopy);
printf("%s\n", buffer);
}
va_end(itemcopy);
}
int
log_print(const char *item, ...)
{
va_list items;
va_start(items, item);
do_log(item, items);
va_end(items);
return 0;
}
int
main(int argc, char **argv)
{
log_print("These %d %d %d %d", 1, 2, 3, 4);
log_print("Hello %s %s", "Mike", argv[0]);
}
Without -DEVIL, I get:
These 1 2 3 4
Hello Mike ./vargs
if we make with CFLAGS=-DEVIL, I get output (on OSX):
These 4 0 0 1570641464
Hello
On other platforms, it can crash.
Related
I have the following code to append a buffer using vsnprintf().
#include <stdio.h>
#include <stdarg.h>
using namespace std;
void MyPrintFunc(char *buffer, size_t sizeOfBuffer, const char* format, ...)
{
va_list arg;
va_start(arg, format);
vsnprintf(buffer, sizeOfBuffer, format, arg);
va_end(arg);
}
int main()
{
char buffer[1000] = { 0 };
const char* abc = "abc";
const char* def = "def";
MyPrintFunc(buffer, 1000, "%s", abc);
MyPrintFunc(buffer, 1000, "%s%s", buffer, def);
printf("%s\n", buffer);
return 0;
}
It gave different output on both Windows and Linux.
On Windows (using msvc-14.0 compiler), it give desired output of abcdef.
But on Linux (using gcc-5.4), it only print output of def.
How can I get the correct output?
Don't use the same buffer as both an output and input to vsnprintf.
Thanks everyone for the input.
My solution to this is to create a copy of the buffer if user of the MyPrintFunc() used it to append buffer:
void MyPrintFunc(char *buffer, size_t sizeOfBuffer, const char* format, ...)
{
va_list arg;
va_list arg_copy;
va_start(arg, format);
va_copy(arg_copy, arg);
if (buffer == va_arg(arg, char*))
{
char temp[strlen(buffer)];
strcpy(temp, buffer);
MyPrintFunc(buffer, sizeOfBuffer, format, temp, va_arg(arg,char*));
}
else
{
vsnprintf(buffer, sizeOfBuffer, format, arg_copy);
}
va_end(arg);
va_end(arg_copy);
}
I found a way to pass a variable length array in C++. But it fails 'wrap' function in below code. Actually I want to wrap format function in my project.
What am i doing wrong in my code?
test code
#include <iostream>
#include <stdarg.h>
#include <string>
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
int length = _vscprintf(format, argptr);
char* buf_ = new char [length + 1];
int ret = vsnprintf(buf_, 1000, format, argptr);
if (ret >= 0) {
std::cout << buf_ << std::endl;
}
delete[] buf_;
va_end(argptr);
}
void wrap(const char *format, ...)
{
va_list ap;
va_start(ap, format);
log(format, ap);
va_end(ap);
}
int main()
{
log( "direct = %d", 1);
wrap("wrap = %d", 1);
return 0;
}
the result is here.
direct = 1
wrap = 15137088 // what's happen?
I found a way to pass a variable length array in C++
That isn't a variable-length array, and it isn't really idiomatic C++. The ... is a variable-length argument list, and is available in C.
The simplest reasonable way to wrap your log function is the variadic template one, which can simply be written as:
template <typename... Args>
void wrap(const char *format, Args&&... args) {
log(format, std::forward<Args>(args)...);
}
In the log function itself, vsnprintf returns the number of bytes that would have been written, in the event it fills the buffer. So, you can always just call it once with an optimistic buffer size, and grow the buffer if necessary: you don't need the non-standard _vscprintf. That would look something like:
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
static const size_t DefaultSize = 200;
// pick some value that makes sense ^^ here
char buf[DefaultSize];
int rv = vsnprintf(buf, DefaultSize, format, argptr);
if (rv < 0) {
// we can't return errors with this prototype:
// should it throw?
return;
}
if (rv >= DefaultSize) {
vector<char> dynbuf(rv+1);
rv = vsnprintf(&dynbuf[0], dynbuf.size(), format, argptr);
std::cout << &dynbuf[0] << std::endl;
} else {
std::cout << buf << std::endl;
}
va_end(argptr);
}
Note also that wrap knows the types of all its arguments, but that information is discarded when you call the C-style variadic function log. You might consider Boost.Format as a type-safe alternative - as a bonus, it will manage the buffer for you.
Passing a va_list where a variable number of argumets (x, y, z) is expected isn't designed to work.
To achieve what you want, you need to do something like this:
void log_args(const char* format, va_list& argptr)
{
// I'm unsure on this... you may possibly need to make a separate
// copy of the va_list to pass in to each of _vscprintf and vsnprintf.
va_list second;
va_copy(second, argptr);
int length = _vscprintf(format, argptr);
char* buf_ = new char [length + 1];
int ret = vsnprintf(buf_, 1000, format, second);
if (ret >= 0) {
std::cout << buf_ << std::endl;
}
delete[] buf_;
}
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
log_args(argptr);
va_end(argptr);
}
void wrap(const char *format, ...)
{
va_list ap;
va_start(ap, format);
log_args(format, ap);
va_end(ap);
}
In this example 'wrap' and 'log' appear the same... but I presume you want to do something additional in your real wrap function otherwise why would you be asking this question.
In c++11, you can use variadic templates
#include <iostream>
void tprintf(const char* format) // base function
{
std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
for ( ; *format != '\0'; format++ ) {
if ( *format == '%' ) {
std::cout << value;
tprintf(format+1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
int main()
{
tprintf("direct = %\n", 1);
tprintf("wrap = %\n", 1);
return 0;
}
I've tried a lot of solutions, but none of them is working.
printf("Test : %d", 123);
std::cout << "Test" << 123 << std::endl;
...
Actually I've setup my project's debugger like this, I'm able to write in the console using OutputDebugStringA("Test"); but this function doesn't accept more than one parameter.
How can I print something like this : ("Test : %d", 123)?
Bogy's answer is on the right track but I don't like the allocation in GetWC which looks like a memory leak, and it uses unsafe functions, try this:
void Debug::Log(const wchar_t *format, ...)
{
wchar_t buffer[BUFFER_LENGTH];
va_list args;
va_start(args, format);
_vsnwprintf_s(buffer, BUFFER_LENGTH, _TRUNCATE, format, args);
va_end(args);
buffer[BUFFER_LENGTH - 1] = '\0'; //prevent buffer overflow
OutputDebugString(buffer);
}
Usage like:
Debug::Log(L"Hello %S\n", "sailor");
I found this code in a C++ training on Pluralsight that I am using for Windows 8 (you just say TRACE("Test : %d", 123); to use it):
#pragma once
#include <assert.h>
#define ASSERT assert
#ifdef _DEBUG
inline auto Trace(wchar_t const * format, ...) -> void
{
va_list args;
va_start(args, format);
wchar_t buffer[256];
ASSERT(-1 != _vsnwprintf_s(buffer, _countof(buffer) - 1, format, args));
va_end(args);
OutputDebugString(buffer);
}
#endif
struct Tracer
{
char const * m_filename;
unsigned m_line;
Tracer(char const * filename, unsigned const line) :
m_filename{ filename },
m_line{ line }
{
}
template <typename... Args>
auto operator()(wchar_t const * format, Args... args) const -> void
{
wchar_t buffer[256];
auto count = swprintf_s(buffer, L"%S(%d): ", m_filename, m_line);
ASSERT(-1 != count);
ASSERT(-1 != _snwprintf_s(buffer + count, _countof(buffer) - count, _countof(buffer) - count - 1, format, args...));
OutputDebugString(buffer);
}
};
#ifdef _DEBUG
#define TRACE Tracer(__FILE__, __LINE__)
#else
#define TRACE __noop
#endif
I've finally found this solution (here and here) :
Debug.cpp:
#pragma once
#include "pch.h"
#define BUFFER_LENGTH 1024
//char* to wchar_t*
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;
}
void Debug::Log(const char *format, ...)
{
char buffer[BUFFER_LENGTH];
va_list args;
va_start(args, format);
vsnprintf(buffer, BUFFER_LENGTH, format, args);
va_end(args);
buffer[BUFFER_LENGTH - 1] = '\0'; //prevent buffer overflow
OutputDebugString(GetWC(buffer));
}
Debug.h:
class Debug{
public:
static void Log(const char *format, ...);
};
We can use it like printf function:
Debug::Log("Test : %d", 123);
I've written a custom print function. My problem is that I need to return a const char* as this has to be used in another function. I simply have no idea how to manage that...
anotherFunction (const char* text /*Here*/, unsigned __int32 value, unsigned __int64 bigVal);
I know the following example/s do/es not work as it should. That's what I've tried so far.
const char* CatchMessage (const char *message, ...)
{
va_list args;
va_start (args, message);
/*?*/
va_end (args);
return message;
}
I've yet only managed to get the correct output in cmd, but I actually need it as return value.
void CatchMessage (const char *message, ...)
{
va_list args;
va_start (args, message);
vfprintf (stdout, message, args);
va_end (args);
}
Call:
CatchMessage ("Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
Should return:
"Some Input stuff and 12 equals to 6"
I have not been able to find a solution. Any help would be appreciated.
Q: How do I get this CatchMessage function to return the correctly formatted const char* ?
It sounds like CatchMessage should take a pointer to a char buffer (and its size), and vsnprintf() into that buffer.
Since you're using C++ (at least according to the tags on the question) why not just return the string in a std::string ?
The problem with returning a (const) char * is that you have to have a buffer somewhere.
There are several ways to accomplish this:
The caller has to provide that buffer
You have to malloc() it
The function itself as a static buffer, but that would make it non-reentrant - which would be bad for multithreading etc.
Ad 1:
void CatchMessage(char * result, size_t maxlen, const char *message, ...)
{
va_list ap;
va_start(ap, message);
vsnprintf(result, maxlen, message, ap);
va_end(ap);
}
called with
char buffer[500];
CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
anotherfunction(buffer, ...)
ad 2:
char * CatchMessage(const char *message, ...)
{
size_t size = 500;
char * result = malloc(size);
if (!result) return NULL; // error handling!
while (1) {
va_list ap;
va_start(ap, message);
size_t used = vsnprintf(result, size, message, ap);
va_end(ap);
char * newptr = realloc(result, size);
if (!newptr) { // error
free(result);
return NULL;
}
result = newptr;
if (used <= size) break;
size = used;
}
return result;
}
called with
char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
if (!buffer) { /* error handling: no memory! */ }
anotherfunction(buffer, ...)
free(buffer); // important for avoiding memory leaks
ad 3:
char * CatchMessage(const char *message, ...)
{
static char result[500]; // static is important here! Otherwise the memory will be freed immediately after returning.
va_list ap;
va_start(ap, message);
vsnprintf(result, sizeof result, message, ap);
va_end(ap);
return result;
}
called with
char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
anotherfunction(buffer, ...)
There is no other option, especially not defining
char result[500];
in the function and then returning it: this array lives on the stack and is freed immediately after return. It cannot be safely accessed by the caller; its contents are just undefined.
If you do not care abour re-entrancy, you could return pointer to static buffer:
#define MESSAGE_MAX 1024
const char *
CatchMessage (const char *message, ...)
{
static buffer[MESSAGE_MAX];
va_list args;
va_start (args, message);
vsnprintf (buffer, MESSAGE_MAX, message, args);
va_end (args);
return buffer;
}
Notes:
This implementation is not thread-safe. If you care about thread safety, use thread local storage instead of static buffer
This implementation has hardcoded upper limit on message length. If that's not desirable, and your compiler is C99 compliant, you could call first vsprintf with NULL as first argument to know result string length, then allocate buffer of that side.
In C++, the use of variadic arguments is bad because there is a certain emphasis on type safety (and memory safety). So, you may want to propose a version that is actually type safe (and yes, that is possible), and yet proposes a similar interface:
template <typename H, typename... Args>
void format(std::ostream& out,
char const* format,
size_t len,
H const& head,
Args const&... args);
// Variations with 'char const (&)[N]' and 'std::string' formats
// as well as variations returning directly a 'std::string'.
Now, the implementation of format is not too difficult; especially without support for positional arguments. A simplistic one can be found below:
inline void format_string(std::ostream& out, char const* const* c) { out << *c; }
inline void format_string(std::ostream& out, std::string const* s) { out << *s; }
inline void format_string(std::ostream& out, void const*); // will throw
template <typename Integral, typename = enable_integral<Integral>::type>
inline void format_integral(std::ostream& out, Integral const* i) { out << *i; }
inline void format_integral(std::ostream& out, void const*); // will throw
inline size_t format_consume(std::ostream& out,
char const* const format,
size_t const length)
{
char const* end = format + length;
char const* current = format;
do {
// 1. Find first "format identifier", output stuff in-between
char const* perc = std::find(current, end, '%');
if (perc != current) { out.write(current, perc - current); }
current = perc;
// 2. %% is % escaped by %, so output it directly
while (*current == '%' and *(current + 1) == '%') {
out.put('%');
current += 2;
}
} while (current != end and *current != '%');
// 3. Return number of characters of format parameter consumed
return current - format;
} // format_consume
inline void format(std::ostream& out, char const* format, size_t len) {
size_t const consumed = format_consume(out, format, len);
if (consumed != len) { throw std::runtime_exception("Missing arguments"); }
} // format
template <typename H, typename typename... Args>
void format(std::ostream& out,
char const* format,
size_t len,
H const& head,
Args const&... args)
{
size_t const consumed = format_consume(out, format, len);
if (consumed == len) { throw std::runtime_exception("Extraneous arguments"); }
format += consumed;
len -= consumed;
assert(*format == '%');
switch(*(format+1)) {
case 's': format_string(out, &head); break;
case 'd': format_integral(out, &head); break;
default: throw std::runtime_exception("Invalid specifier");
}
format(out, format+2, len-2, args...);
} // format
In the general case, it is slightly more hairy, as you need to parse the modifiers, etc... but then, for a production-ready implementation, I advise a look at Boost.Format.
char* CatchMessage (size_t size, const char *message, ...)
{
char result[size];
va_list args;
va_start (args, message);
vsprintf (result, message, args);
va_end (args);
return result;
}
See this for reference.
And for glglgl: If you want to use the clean way, use vsnprintf(result, size, message, args)
I am porting an application on Mac OS X which was written for Windows.
In this application, there are many instances of _vscwprintf and _vscprintf.
This question helped me to implement _vsprintf on Mac OS X. But same technique for _vswprintf is not working.
Can anyone give the alternative of _vscwprintf on Mac OS X? Or there any equivalent method for this?
Microsoft describes the functions as returning the number of characters that would be used if the string were formatted — note that they are documented as not including the null terminator.
int _vscprintf(
const char *format,
va_list argptr
);
int _vscwprintf(
const wchar_t *format,
va_list argptr
);
Initial answer
These functions can, therefore, be emulated with vsprintf() and vswprintf():
int _vscprintf(const char *format, va_list argptr)
{
return(vsnprintf(0, 0, format, argptr));
}
int _vscwprintf(const wchar_t *format, va_list argptr)
{
return(vswprintf(0, 0, format, argptr));
}
It is up to you whether you remove the leading underscore; I would.
Note that the _vscwprintf() implementation above is flawed; see the code below.
vscprintf() and scprintf()
Apologies: I wrote vsprintf() where I needed to write vsnprintf() (now fixed in the code above); however, vswprintf() already has the safer interface with the buffer length, so there is no vsnwprintf(). There's a reason I prefer to test compile code before (or shortly after) posting it — it's been irksome not having the wherewithal to do so for a couple of days.
Here's an SSCCE for vscprintf() (and scprintf()):
#include <stdio.h>
#include <stdarg.h>
extern int vscprintf(const char *format, va_list argptr);
extern int scprintf(const char *format, ...);
int vscprintf(const char *format, va_list argptr)
{
return(vsnprintf(0, 0, format, argptr));
}
int scprintf(const char *format, ...)
{
va_list args;
va_start(args, format);
int rc = vscprintf(format, args);
va_end(args);
return rc;
}
int main(void)
{
int l = scprintf("%-8s %8d\n", "abc", 123);
if (l > 0)
{
char buffer[l+1];
int n = snprintf(buffer, sizeof(buffer), "%-8s %8d\n", "abc", 123);
printf("%d = %d: %s", l, n, buffer);
}
return 0;
}
Output:
18 = 18: abc 123
vscwprintf() and scwprintf()
It turns out to be harder to simulate _vscwprintf() because the vswprintf() function is not as helpful as the vsnprintf() function. Specifically, vswprintf() reports an error if the formatted string won't fit in the formatted space, whereas vsnprintf() reports the number of characters that would have been needed in the buffer if it was going to fit. Hence, you have to work by trial and error:
#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>
extern int vscwprintf(const wchar_t *format, va_list argptr);
extern int scwprintf(const wchar_t *format, ...);
int vscwprintf(const wchar_t *format, va_list argptr)
{
// Unlike vsnprintf(), vswprintf() does not tell you how many
// characters would have been written if there was space enough in
// the buffer - it just reports an error when there is not enough
// space. Assume a moderately large machine so kilobytes of wchar_t
// on the stack is not a problem.
int buf_size = 1024;
while (buf_size < 1024 * 1024)
{
va_list args;
va_copy(args, argptr);
wchar_t buffer[buf_size];
int fmt_size = vswprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), format, args);
if (fmt_size >= 0)
return fmt_size;
buf_size *= 2;
}
return -1;
}
int scwprintf(const wchar_t *format, ...)
{
va_list args;
va_start(args, format);
int rc = vscwprintf(format, args);
va_end(args);
return rc;
}
int main(void)
{
int l = scwprintf(L"%-8ls %8d\n", L"abc", 123);
if (l > 0)
{
wchar_t buffer[l+1];
int n = swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), L"%-8ls %8d\n", L"abc", 123);
wprintf(L"%d = %d: %ls", l, n, buffer);
}
return 0;
}
When run, this produces the output
18 = 18: abc 123
(the same as before).
Tested on Mac OS X 10.8.3 using GCC 4.7.3 (which was built on Mac OS X 10.7.5, but that shouldn't cause any problems).
I recommend using open_wmemstream instead. Something like that:
#include <stdio.h>
#include <string>
#include <stdarg.h>
using namespace std;
wstring wstring_format(const wchar_t* format, ...)
{
wchar_t* buf;
size_t size;
FILE* stream = open_wmemstream(&buf, &size);
va_list args;
va_start(args, format);
vfwprintf(stream, format, args);
va_end(args);
fclose(stream);
wstring result(buf, buf + size);
free(buf);
return result;
}