I have this method (by using code from vsnprintf man page):
MYSQL_RES *nsDatabase::queryf(const char *fmt,...){
int n, size = 1024;
char *query=NULL,*np;
va_list ap;
if (this->dbLink == NULL){
return NULL;
}
query = (char *) malloc (size);
if (query == NULL) return NULL;//memory error
while (1) {
va_start(ap, fmt);
n=vsnprintf(query,size,fmt,ap);
va_end(ap);
if (n > -1 && n < size){// format string succeeded
break;
}
if (n > -1) /* glibc 2.1 */
size = n+1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
np = (char *) realloc (query, size);
if (np == NULL) {
printf("memory error\n");
FREE(query);
return NULL;//again memory error
} else {
query = np;
}
}
MYSQL_RES *r = this->query(query);
FREE(query);
if(r == NULL){
return NULL; //mysql error
}
return mysql_store_result(this->dbLink);
}
I want to make general function queryFormat which will calculate the size needed for formatted string, allocate memory, print format in it and return the string (not real code, pseudo code):
char *queryFormat(const char *fmt, va_list ap){
// allocate memory
// problem - can I use vsnprintf multiple times here (to determine the size of formatted string and allocate memory)
return <formatted string>;
}
Then I need to call it from all functions that format queries (not real code):
queryRow(const char *fmt,...){
va_start(ap, fmt);
// I need to call vsnprintf many times in queryFormat ... Do I need to call va_start() before every call to fsnprintf ?
char * formattedQuery = queryFormat(query, size, fmt, ap);
va_end(ap);
MYSQL_RES *r = this->query(formattedQuery);
free(formattedQuery);
...
}
I write for GCC under linux, but the code should be runnable under MinGW and CygWin.
See this - http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html.There is a good example in the book of Kernighan and Ritchie book.
EDIT
Its the same in C++ too . you can have a look at this too. - http://www.cplusplus.com/reference/clibrary/cstdarg/va_start/
EDIT 2
http://www.cplusplus.com/reference/clibrary/cstdio/vprintf/. I think this shall help you.
I believe this should help anyone who googled here in search of solution to the problem of how to "reuse" a va_list in a function that had it already passed as parameter:
// Incorrect:
/*void g(int foo, va_list ap) {
vprintf("%s %s %s\n", ap);
vprintf("%s %s %s\n", ap);
}*/
void g(int foo, va_list ap) {
va_list copy;
va_copy(copy, ap);
vprintf("%s %s %s\n", copy);
va_copy(copy, ap);
vprintf("%s %s %s\n", copy);
}
void f(int foo, ...) {
va_list ap;
va_start(ap, foo);
g(foo, ap);
va_end(ap);
}
int main() {
f(42, "aaa", "bbb", "ccc");
return 0;
}
Related
A while ago I wrote a logger class under linux and I try to port it under windows (visual studio) and it keeps crashing
first I had to use vsprintf_s intead of vsprintf, cos the comiler rejected it
but the only example I found does not seem to work
"len" contents wrong values instead of real number of arguments
here is how I call my function :
Logger::print("Rock and roll");
or
Logger::print("%4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n", a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y);
original linux code:
void Logger::print(const std::string fmt,...)
{
char formatted_string[2*1024];
strcpy(formatted_string,this->prefix);
strcat(formatted_string," ");
char*strPtr=&formatted_string[0]+strlen(this->prefix)+1;
va_list argptr;
va_start(argptr, fmt );
vsprintf(strPtr,fmt.c_str(), argptr);
va_end(argptr);
printf("%s\n",formatted_string);
}
new windows code:
void Logger::print(const std::string fmt, ...)
{
va_list args;
int len;
char * buffer;
va_start(args, fmt);
len = _vscprintf(fmt.c_str(), args); << len contents odd values instead of real number of arguments
va_end(args);
buffer = (char*)malloc(len * sizeof(char));
vsprintf_s(buffer, len, fmt.c_str(), args);
Logger::file << buffer;
free(buffer);
}
regards
You're prematurely invoking va_end. Fixing that, you're also not calculating the correct target size, as result of _vscprintf does not include space for the terminating null characters, which vsprintf_s will utilize.
The body of your function should look like this, including removing that malloc call in favor of a proper RAII solution using a vector:
va_list args;
va_start(args, fmt);
int len = _vscprintf(fmt.c_str(), args);
if (len > 0)
{
std::vector<char> buff(len + 1); // include terminator space
vsprintf_s(&buff[0], buff.size(), fmt.c_str(), args);
Logger::file << buff.data();
}
va_end(args);
Worth noting: running your original code in the debugger will result in a buffer-too-small exception, which the debugger will catch handily. Debuggers are what's for dinner.
here is the working code
void Logger::print(const std::string fmt, ...)
{
va_list args;
int len;
char * buffer;
va_start(args, fmt);
len = _vscprintf(fmt.c_str(), args)+1;
buffer = (char*)malloc(len * sizeof(char));
vsprintf_s(buffer, len, fmt.c_str(), args);
va_end(args);
Logger::file << buffer;
free(buffer);
}
#define printm(X) handleVarArgs X
void writeFile(std::string & s)
{
FILE *fp;
fopen("test.txt","w");
fputs( s.c_str(), fp );
fclose(fp);
}
void handleVarArgs( char* format, ...)
{
std::string s;
va_list args;
va_start (args, format);
vsprintf (const_cast<char *>(s.c_str()),format, args);
va_end (args);
writeFile(s);
}
const char * func() {
std::string errorLog("Kiev");
return errorLog.c_str();
}
int main()
{
printm(("My Name is %s and surname is %s and age is %d",func(),"john",25));
return 0;
}
I am getting segmentation fault if i call writeFile() function. But no segmentation fault when i remove writeFile()
What is the relationship between Files and Var Args?
You are writing directly in a string.c_str(), simply by removing its constness with a const_cast. This is bad and invoke undefined behaviour. So what happens next is simply undefined ...
The correct approach for handleVarArgs would be :
allocate a bunch of memory from the heap (with malloc or new[])
use vsnprintf to try to write there
if the return value (say sz) is greater or equal to the allocated size, then free it and realloc a bunch of size sz+1 and iterate
then you can safely create a std::string from that char array and use it
do not forger to call free (or delete[]) when finished with the malloc'ed array
Something like :
void handleVarArgs( char* format, ...)
{
#define DEF_SZ 256
int sz = DEF_SZ;
std::string s;
while(1) {
char * buf = new char[sz];
va_list args;
va_start (args, format);
int l = vsnprintf (buf , sz, format, args);
va_end (args);
if (l < sz) {
s = buf;
writeFile(s);
delete[] buf;
return;
}
sz = l + 1;
delete[] buf;
}
}
What is the value of fp after opening a file?
How does it relate to the value from before opening a file?
The debugger is Your best friend.
It should read: FILE *fp = fopen("test.txt","w");
Not mentioning that You should check wether the fopen succeeded.
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.
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 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)