468,512 Members | 1,375 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,512 developers. It's quick & easy.

type-safe varargs

Goal: to provide the simplest call-site possible at the cost of
upfront hackery.

I have a function that essentially takes a variable list of arguments:

func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)

This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.

Options I have found:

Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.
Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)
Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.
Can anyone suggest a better design?
Dec 20 '07 #1
13 3927
On Dec 20, 6:38 am, Tim H <thoc...@gmail.comwrote:
Goal: to provide the simplest call-site possible at the cost of
upfront hackery.

I have a function that essentially takes a variable list of arguments:

func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)

This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.

Options I have found:

Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.

Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)

Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.

Can anyone suggest a better design?
How about an object to encapsulate all the parameters.

Something like (foreshortened example):
struct Params {
string& k1;
string& k2;
ull v1;
ull v2;
Params(const string& k1, const string&k2) : k1(k1), k2(k2), v1(0),
v2(0) {}
Params(const string& k1, ull v1) : k1(k1), v1(v1), v2(0) {}
Params(const string& k1, ull v1, ull v2) : k1(k1), v1(v1), v2(v2) {}
// etc - only need to cover the permutations you use
};

then.

extern void func(const Params&);
:
func(Params("foo", "bar"));
func(Params("baz", 123345ULL));
func(Params("yadda", 123344ULL, 789010ULL));

etc.

Dec 20 '07 #2
On Dec 20, 11:19 am, tragomaskhalos <dave.du.verg...@logicacmg.com>
wrote:
>
Something like (foreshortened example):
struct Params {
string& k1;
string& k2;
Oops. Should be
const string& k1;
const string& k2;
I think this is OK wrt binding-to-temporary lifetime issues,
hopefully someone will shoot me down if not.
Dec 20 '07 #3
On Dec 20, 3:41 am, tragomaskhalos <dave.du.verg...@logicacmg.com>
wrote:
On Dec 20, 11:19 am, tragomaskhalos <dave.du.verg...@logicacmg.com>
wrote:
Something like (foreshortened example):
struct Params {
string& k1;
string& k2;

Oops. Should be
const string& k1;
const string& k2;
I think this is OK wrt binding-to-temporary lifetime issues,
hopefully someone will shoot me down if not.
I still need to provide a constructor that has 32 pairs of (kn, vn) or
32 different constructors. At least with 32 different ctors, I don't
pay the perf cost. It's still hideous :)
Dec 20 '07 #4
On 2007-12-20 01:38:44 -0500, Tim H <th*****@gmail.comsaid:
Goal: to provide the simplest call-site possible at the cost of
upfront hackery.

I have a function that essentially takes a variable list of arguments:

func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)

This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.

Options I have found:

Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.
Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)
Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.
Can anyone suggest a better design?
How about defining the function as an object instead?

class func
{
public:
func &set(const string &key, unsigned long long val)
{
// do whatever you need with the parameter here.
return *this;
}

int execute()
{
// this is the actual "function" call here.
}
};

This code will support syntax like
func.set("foo", 1).set("bar", 2).execute();

Furthermore, if the keys for the parameters are known at compile time,
you can make it more efficient by declaring methods like
func &setFoo(unsigned long long val);
to make runtime more efficient.

--

-kira

Dec 20 '07 #5
On Dec 19, 10:38 pm, Tim H <thoc...@gmail.comwrote:
Goal: to provide the simplest call-site possible at the cost of
upfront hackery.

I have a function that essentially takes a variable list of arguments:

func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)

This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.

Options I have found:

Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.

Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)

Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.

Can anyone suggest a better design?

void func(const std::vector<boost::any&);

or if you want to change the arguments:
void func(std::vector<boost::any&);

upside is that boost::any can encapsulate anything with a copy
constructor including POD types.
You could write simple functions for any possible argument combination
which return a vector, e.g.
std::vector<boost::anycreateVectorByStringAndInt(c onst std::string
&_r0, const int _i1)
{ std::vector<boost::anys;

s.push_back(_r0);
s.push_back(_i1);
return s;
}

then you could do
std::string s0;
int i1;
func(createVectorByStringAndInt(s0, i1));

Be aware that using boost::any is not free.
When creating a boost::any an object is allocated on the heap and the
passed object is copy-constructed.
Same for copying boost::any.
Dec 21 '07 #6
On Dec 19, 10:38 pm, Tim H <thoc...@gmail.comwrote:
Goal: to provide the simplest call-site possible at the cost of
upfront hackery.

I have a function that essentially takes a variable list of arguments:

func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)

This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.

Options I have found:

Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.

Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)

Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.

Can anyone suggest a better design?
func(const std::vector<std::pair<std::string, unsigned long long
&_r);
Dec 21 '07 #7
On Dec 20, 4:14 pm, Peter <ExcessPh...@gmail.comwrote:
On Dec 19, 10:38 pm, Tim H <thoc...@gmail.comwrote:
Goal: to provide the simplest call-site possible at the cost of
upfront hackery.
I have a function that essentially takes a variable list of arguments:
func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)
This is going to be called a LOT from code that is hand-written, so I
really ant to simplify the job of calling this function.
Options I have found:
Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the downside
of being limited to built-in types - char * rather than string. It
also has the downside of forcing the caller to make sure literal ints
are ULL, lest we unbalance the stack.
Use C pre-processor hackery to get __VA_ARGS__ to initialize an array
arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)
Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.
Can anyone suggest a better design?

func(const std::vector<std::pair<std::string, unsigned long long
&_r);
And how does the caller call that? You can't declare a vector the
same way you can an array.

I want to see the call-site be something like:
func("key1", 1, "key2", 2, "key3, 99, "key4", -12345678);

I know it sounds silly, but trust me, it makes my life a whole lot
easier, the simpler it is.
Dec 21 '07 #8
Tim H wrote:
On Dec 20, 4:14 pm, Peter <ExcessPh...@gmail.comwrote:
>On Dec 19, 10:38 pm, Tim H <thoc...@gmail.comwrote:
>>Goal: to provide the simplest call-site possible at the cost of
upfront hackery.
>>I have a function that essentially takes a variable list of
arguments:
>> func(const string &k0, unsigned long long v0, const string &k1,
unsigned long long v1, etc)
>>This is going to be called a LOT from code that is hand-written, so
I really ant to simplify the job of calling this function.
>>Options I have found:
>>Use varargs.
func(...)
This has the plus of being simple and unlimited. It has the
downside of being limited to built-in types - char * rather than
string. It also has the downside of forcing the caller to make
sure literal ints are ULL, lest we unbalance the stack.
>>Use C pre-processor hackery to get __VA_ARGS__ to initialize an
array arg.
#define FUNC(...) FUNC_((kvpair[]){__VA_ARGS__, {NULL}})
This falls down as soon as you use anything but a literal in the
call. And the syntax confuses every syntax coloring app I have
seen :)
>>Use default args.
func(const string &k0, unsigned long long v0,
const string &k1="", unsigned long long v1=0,
const string &k2="", unsigned long long v2=0,
const string &k3="", unsigned long long v3=0,
const string &k4="", unsigned long long v4=0)
This has the upside that it is type-safe and simple to call. It has
the downside that I need to extend the args list to cover the worst
case caller, and pay that cost at every call.
>>Can anyone suggest a better design?

func(const std::vector<std::pair<std::string, unsigned long long
&_r);

And how does the caller call that? You can't declare a vector the
same way you can an array.

I want to see the call-site be something like:
func("key1", 1, "key2", 2, "key3, 99, "key4", -12345678);

I know it sounds silly, but trust me, it makes my life a whole lot
easier, the simpler it is.
But varargs is not simple is it?

--
Jim Langston
ta*******@rocketmail.com
Dec 21 '07 #9
Dredging up an old thread with a new answer. I wish I was less proud
of this. Is __VA_ARGS__ standard C++ or just C99?

#include <vector>
#include <iostream>

// do whatever you need in your helper
struct helper
{
public:
helper(const std::string &str): m_str(str)
{
}
const std::string &
str() const
{
return m_str;
}

private:
std::string m_str;
};

typedef std::vector<helperhelper_list;

inline helper_list
operator,(const helper &lhs, const helper &rhs)
{
helper_list tmp;
tmp.push_back(lhs);
tmp.push_back(rhs);
return tmp;
}
inline helper_list
operator,(const helper_list &lhs, const helper &rhs)
{
helper_list tmp(lhs);
tmp.push_back(rhs);
return tmp;
}

/*
* unlimited_args()
*
* It can take an "unlimited" number of arguments, in the form:
* unlimited_args((helper("abc"), helper("def")))
*
* NOTE: In calling this, the extra parens are required. Why?
* Well, C++ won't call operator,() on function arguments, so we force
* the arguments to be a single argument, which is an expression.
* Fortunately, a touch of macro magic makes it look like an unlimited
* argument list.
*/
#define unlimited_args(name, ...) unlimited_args_impl(name,
(__VA_ARGS__))

void
unlimited_args_impl(const std::string &name, const helper_list &args)
{
std::cout << name << std::endl;
for (std::size_t i = 0; i < args.size(); i++) {
std::cout << " " << args[i].str() << std::endl;
}
}
// With a smarter custom helper_list (less than 30 LOC), this overload
can be
// avoided.
void
unlimited_args_impl(const std::string &name, const helper &arg)
{
helper_list hl;
hl.push_back(arg);
unlimited_args_impl(name, hl);
}

int
main()
{
// these calls can take any number of helper args
unlimited_args("small", helper("a"));
unlimited_args("medium", helper("a"), helper("b"));
unlimited_args("large", helper("a"), helper("b"),
helper("c"));

return 0;
}

Jan 18 '08 #10
Tim H wrote:
Dredging up an old thread with a new answer. I wish I was less proud
of this. Is __VA_ARGS__ standard C++ or just C99?
Variadic macros are C99 (not sure of the exact C99 syntax), and I
believe they're slated for C++0x. However, the current (C++03)
preprocessor does not support variadic macros.
Jan 18 '08 #11
On Jan 18, 8:11 am, red floyd <no.s...@here.dudewrote:
Tim H wrote:
Dredging up an old thread with a new answer. I wish I was less proud
of this. Is __VA_ARGS__ standard C++ or just C99?

Variadic macros are C99 (not sure of the exact C99 syntax), and I
believe they're slated for C++0x. However, the current (C++03)
preprocessor does not support variadic macros.
I compiled the above code on gcc, so no surprise. Even so, the
calling syntax could change to:

unlimited_args("foo" (helper("a"), helper("b"), helper("c"));

Tim
Jan 18 '08 #12
Tim H wrote:
On Jan 18, 8:11 am, red floyd <no.s...@here.dudewrote:
>Tim H wrote:
Dredging up an old thread with a new answer. I wish I was less proud
of this. Is __VA_ARGS__ standard C++ or just C99?

Variadic macros are C99 (not sure of the exact C99 syntax), and I
believe they're slated for C++0x. However, the current (C++03)
preprocessor does not support variadic macros.

I compiled the above code on gcc, so no surprise.
In standard C++ mode or in GNU C++ mode?

Jan 20 '08 #13
On Jan 20, 1:52 am, Rolf Magnus <ramag...@t-online.dewrote:
Tim H wrote:
On Jan 18, 8:11 am, red floyd <no.s...@here.dudewrote:
Tim H wrote:
Dredging up an old thread with a new answer. I wish I was less proud
of this. Is __VA_ARGS__ standard C++ or just C99?
Variadic macros are C99 (not sure of the exact C99 syntax), and I
believe they're slated for C++0x. However, the current (C++03)
preprocessor does not support variadic macros.
I compiled the above code on gcc, so no surprise.

In standard C++ mode or in GNU C++ mode?
I did not specify any g++ options except -Wall
Jan 20 '08 #14

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

21 posts views Thread by Batista, Facundo | last post: by
6 posts views Thread by S.Tobias | last post: by
reply views Thread by Chris Fink | last post: by
1 post views Thread by Rob Griffiths | last post: by
669 posts views Thread by Xah Lee | last post: by
5 posts views Thread by JH | last post: by
1 post views Thread by fmendoza | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.