I\'m using gcc 4.6. Assume that there is a vector v of parameters I have to pass to a variadic function f(const char* format, ...).
One approach of doing this is:
Okay, here is a partial solution! Partial, because it does not apply to really variadic functions, but to those which accept a va_list as argument. But I think the full solution isn't far away.
It is based on the examples I found here:
Dynamically create a va_list https://bbs.archlinux.org/viewtopic.php?pid=238721
forge a va_list http://confuseddevelopment.blogspot.com/2006/04/dynamically-creating-valist-in-c.html
This code is tested with gcc on linux and VC++2008 successfully, other platforms might be supported too, but that's up to you.
The important insight for me was that a va_list is basically nothing more than a packed array, which can be filled with data dynamically and can be passed to functions like vprintf, vfprintf, vsprintf which accept it as argument.
So passing vector items to one of those functions can work by allocating enough memory for the vector items and copy them over prior to the call.
Having said that, here is the dynamically allocating stack approach:
#include
#include
#include
#include
#include
#include
using namespace std;
class Format
{
typedef vector ULVector;
ULVector _args;
string _format;
public:
Format(const char* format) : _format(format)
{}
Format &operator<<(int arg) {
_args.push_back((unsigned long)arg);
return *this;
}
Format &operator<<(const char* arg) {
_args.push_back((unsigned long)arg);
return *this;
}
string format() {
union {
va_list varargs;
unsigned long* packedArray;
} fake_va_list;
// malloc would do it as well!
// but alloca frees the mem after leaving this method
unsigned long *p = (unsigned long*)alloca(_args.size() * sizeof(unsigned long));
fake_va_list.packedArray = p;
ULVector::iterator i = _args.begin();
for (int n=0; i != _args.end(); i++, n++) {
p[n] = *i;
}
char buffer[512];
const char* fmt = _format.c_str();
vsprintf(buffer, fmt, fake_va_list.varargs);
// place a free(p) here if you used malloc
return string(buffer);
}
};
ostream& operator <<=(ostream &os, Format &obj) {
os << obj.format();
return os;
}
int main()
{
// we use '<<=' operator here which has lower precedence than '<<'
// otherwise we have to write
// cout << ( Format("\n%x %s %x %c\n") << etc. );
cout <<= Format("\n%x %s %x %c\n") << 0x11223344 << "VectorToVarArg" << 0xAABBCCDD << '!';
return 0;
}
Guess what it does? It allows printf(..) style formatting with the parameters collected in a vector. Yes, it's not perfect but it does what I wanted. Furthermore, it covers two major platforms :D
Additionally, have look at this article: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a