using vsprintf and file operations - c++

#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.

Related

Problem using variable argument list and string formating

I have the following function that accepts a string and a list of arguments, the idea is that it works as a printf for the user. The function below is a minimal executable example of the function.
string description = "";
void error(string format, ...){
va_list arguments;
va_start(arguments, format);
va_list arg_copy;
va_copy(arg_copy, arguments);
const int32_t size = snprintf(nullptr, 0, format.c_str(), arguments) + 1;
const unique_ptr<char[]> buffer = make_unique<char[]>(size);
va_end(arguments);
snprintf(buffer.get(), size, format.c_str(), arg_copy);
va_end(arg_copy);
description += string(buffer.get(), buffer.get() + size - 1);
}
And I call it as follows.
int main()
{
int a = 123;
error("Format %d", a);
cout<< description;
return 0;
}
The expected output is: Format 123
The output result is (the number changes each execution): Format 378812424
I guess it is some problem with the memory but I am not able to discover where the problem is.
You are using the wrong print function. By using snprintf(), you are printing the address of the va_list itself, not the values that it refers to.
To print a va_list, you need to use vsnprintf() instead, eg:
string description = "";
void error(string format, ...){
va_list arguments;
va_start(arguments, format);
va_list arg_copy;
va_copy(arg_copy, arguments);
const int32_t size = vsnprintf(nullptr, 0, format.c_str(), arg_copy) + 1;
va_end(arg_copy);
vector<char> buffer(size);
vsnprintf(buffer.data(), size, format.c_str(), arguments);
va_end(arguments);
description.append(buffer.data(), size - 1);
}

Is there an analogous function to `vsnprintf` that works with `std::string`

I don't see a constructor for std::string that can consume a va_list. Is there a common solution for converting a va_list to a std::string?
I've seen solutions in the form of:
std::string vstring (const char * format, ...) {
std::string result;
va_list args;
va_start(args, format);
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, args);
result = std::string(buffer);
va_end(args);
return result;
}
This feels error prone and hacky. Is there a way for std::string to be constructed from or operate on a va_list directly?
NOTE: The main problem I have with the solution above is the need to guess at the amount of memory I need. I don't want to waste too much or not have enough. Ideally, I would like a std::string style opaque allocation that just works.
NOTE: I need a solution that does not require third party library support.
vsnprintf() can calculate the needed buffer size without actually outputting to a buffer, so you usually don't need a separate char[] at all, you can just calculate the size, allocate the std::string to that size, and then use the std::string's own internal buffer for the output, eg:
std::string vstring (const char * format, ...)
{
std::string result;
va_list args, args_copy;
va_start(args, format);
va_copy(args_copy, args);
int len = vsnprintf(nullptr, 0, format, args);
if (len < 0) {
va_end(args_copy);
va_end(args);
throw std::runtime_error("vsnprintf error");
}
if (len > 0) {
result.resize(len);
// note: &result[0] is *guaranteed* only in C++11 and later
// to point to a buffer of contiguous memory with room for a
// null-terminator, but this "works" in earlier versions
// in *most* common implementations as well...
vsnprintf(&result[0], len+1, format, args_copy); // or result.data() in C++17 and later...
}
va_end(args_copy);
va_end(args);
return result;
}
Though, prior to C++11, using a separate buffer would be a more "correct" (ie, portable) and safer choice, eg:
std::string vstring (const char * format, ...)
{
std::string result;
va_list args, args_copy;
va_start(args, format);
va_copy(args_copy, args);
int len = vsnprintf(nullptr, 0, format, args);
if (len < 0) {
va_end(args_copy);
va_end(args);
throw std::runtime_error("vsnprintf error");
}
if (len > 0) {
std::vector<char> buffer(len+1);
vsnprintf(&buffer[0], buffer.size(), format, args_copy);
result = std::string(&buffer[0], len);
}
va_end(args_copy);
va_end(args);
return result;
}
You can use the fact that snprintf can be used with nullptr buffer and size 0 to get resulting buffer size and write the message into the std::string itself.
Note that va_copy should be used if you want to reuse va_list.
std::string vformat(const char *format, va_list args)
{
va_list copy;
va_copy(copy, args);
int len = std::vsnprintf(nullptr, 0, format, copy);
va_end(copy);
if (len >= 0) {
std::string s(std::size_t(len) + 1, '\0');
std::vsnprintf(&s[0], s.size(), format, args);
s.resize(len);
return s;
}
const auto err = errno;
const auto ec = std::error_code(err, std::generic_category());
throw std::system_error(ec);
}
std::string format(const char *format, ...)
{
va_list args;
va_start(args, format);
const auto s = vformat(format, args);
va_end(args);
return s;
}

How do I pass a variable length array in a function which receives a variable length array in C++?

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;
}

Return a const char* from a custom printf function

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)

Variable argument lists in C++

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;
}